Merge
This commit is contained in:
commit
1be67ea008
|
@ -36,8 +36,23 @@ jobs:
|
|||
with:
|
||||
modules: 'qtcharts'
|
||||
|
||||
- name: Test CMake build
|
||||
run: |
|
||||
mkdir cmake.output
|
||||
cd cmake.output
|
||||
cmake -G "Unix Makefiles" -DBUILD_TESTS=On ..
|
||||
make -j$(nproc) check
|
||||
cd ..
|
||||
|
||||
- name: Unsigned char
|
||||
run: |
|
||||
make clean
|
||||
make -j$(nproc) CXXFLAGS=-funsigned-char testrunner
|
||||
./testrunner TestSymbolDatabase
|
||||
|
||||
- name: Build cppcheck
|
||||
run: |
|
||||
make clean
|
||||
cp externals/z3_version_old.h externals/z3_version.h
|
||||
make -j$(nproc) USE_Z3=yes HAVE_RULES=yes
|
||||
|
||||
|
@ -60,6 +75,41 @@ jobs:
|
|||
qmake HAVE_QCHART=yes
|
||||
make -j$(nproc)
|
||||
|
||||
- name: Run GUI tests on ubuntu
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: |
|
||||
pushd gui/test/projectfile
|
||||
qmake
|
||||
make -j$(nproc)
|
||||
./test-projectfile
|
||||
|
||||
# Run self check after "Build GUI" to include generated headers in analysis
|
||||
- name: Self check
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: |
|
||||
# compile with verification and ast matchers
|
||||
make clean
|
||||
make -j$(nproc) -s CPPFLAGS="-DCHECK_INTERNAL" CXXFLAGS="-g -O2" MATCHCOMPILER=yes VERIFY=1
|
||||
# self check lib/cli
|
||||
mkdir b1
|
||||
./cppcheck -q -j$(nproc) --template=gcc --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/tinyxml/ -Icli --inconclusive --enable=style,performance,portability,warning,internal --exception-handling cli lib
|
||||
# check gui with qt settings
|
||||
mkdir b2
|
||||
./cppcheck -q -j$(nproc) --template=gcc --cppcheck-build-dir=b2 -D__CPPCHECK__ -DQT_VERSION=0x050000 --error-exitcode=1 --inline-suppr --suppressions-list=.travis_suppressions --library=qt --addon=naming.json -Ilib -Iexternals/simplecpp/ -Iexternals/tinyxml/ --enable=style,performance,portability,warning,internal --exception-handling gui/*.cpp
|
||||
# self check test and tools
|
||||
./cppcheck -q -j$(nproc) --template=gcc -D__CPPCHECK__ --error-exitcode=1 --inline-suppr --suppressions-list=.travis_suppressions --library=cppcheck-lib -Ilib -Iexternals/simplecpp/ -Iexternals/tinyxml/ -Icli -Igui --inconclusive --enable=style,performance,portability,warning,internal --exception-handling test/*.cpp tools
|
||||
|
||||
- name: Build triage on ubuntu
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: |
|
||||
pushd tools/triage
|
||||
qmake
|
||||
make -j$(nproc)
|
||||
|
||||
- name: Fuzzer
|
||||
run: |
|
||||
g++ -fsyntax-only -std=c++11 -Ilib oss-fuzz/*.cpp
|
||||
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: cppcheck_cli
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
# 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:
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
fail-fast: false # not worthwhile...
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
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 z3 libz3-dev
|
||||
cp externals/z3_version_old.h externals/z3_version.h
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
cp externals/z3_version_old.h externals/z3_version.h
|
||||
CXXFLAGS="-fsanitize=address -Og -g3" make cppcheck testrunner -j$(nproc) USE_Z3=yes
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
./testrunner
|
||||
|
||||
- name: Bughunting lib
|
||||
run: |
|
||||
./cppcheck -D__CPPCHECK__ --bug-hunting -j$(nproc) lib
|
||||
|
|
@ -1,20 +1,11 @@
|
|||
name: "IRC Push Notification"
|
||||
on: [push, pull_request, create]
|
||||
on: [pull_request, create]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
if: github.repository == 'danmar/cppcheck'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: irc push
|
||||
uses: rectalogic/notify-irc@v1
|
||||
if: github.event_name == 'push'
|
||||
with:
|
||||
channel: "#cppcheck"
|
||||
nickname: cppcheck-github-notifier
|
||||
message: |
|
||||
${{ github.actor }} pushed ${{ github.event.ref }} ${{ github.event.compare }}
|
||||
${{ join(github.event.commits.*.message) }}
|
||||
- name: irc pull request
|
||||
uses: rectalogic/notify-irc@v1
|
||||
if: github.event_name == 'pull_request'
|
||||
|
|
|
@ -38,3 +38,11 @@ jobs:
|
|||
pylint --rcfile=pylintrc_travis htmlreport/*.py
|
||||
pylint --rcfile=pylintrc_travis tools/*.py
|
||||
|
||||
- name: compile addons
|
||||
run: |
|
||||
python -m compileall ./addons
|
||||
python3 -m compileall ./addons
|
||||
|
||||
- name: check .json files
|
||||
run: |
|
||||
find . -name '*.json' | xargs -n 1 python3 -m json.tool > /dev/null
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
# 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:
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
fail-fast: false # not worthwhile...
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
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 z3 libz3-dev
|
||||
cp externals/z3_version_old.h externals/z3_version.h
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
cp externals/z3_version_old.h externals/z3_version.h
|
||||
CXXFLAGS="-fsanitize=undefined -Og -g3" make cppcheck testrunner -j$(nproc) USE_Z3=yes
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
./testrunner
|
||||
|
||||
- name: checkcfg
|
||||
run: |
|
||||
make checkcfg
|
||||
|
105
.travis.yml
105
.travis.yml
|
@ -20,7 +20,7 @@ env:
|
|||
before_install:
|
||||
# install needed deps
|
||||
- travis_retry sudo apt-get update -qq
|
||||
- travis_retry sudo apt-get install -qq python3-pip qt5-default qt5-qmake qtbase5-dev qtcreator qttools5-dev qttools5-dev-tools 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
|
||||
- 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 pylint
|
||||
|
@ -44,36 +44,8 @@ matrix:
|
|||
allow_failures:
|
||||
- name: "rerun dmake?"
|
||||
compiler: clang
|
||||
- name: "make ubuntu 14.04 trusty"
|
||||
# defined extra jobs that run besides what is configured in the build matrix
|
||||
include:
|
||||
# -fsanitize=undefined
|
||||
- name: "undefined behaviour sanitizers"
|
||||
compiler: gcc
|
||||
script:
|
||||
- CXXFLAGS="-fsanitize=undefined -fno-sanitize-recover=all -Og -g3" make cppcheck check checkcfg -j 2 -s
|
||||
# -fsanitize=address
|
||||
- name: "address sanitizers"
|
||||
compiler: gcc
|
||||
script:
|
||||
- CXXFLAGS="-fsanitize=address -Og -g3" make cppcheck check checkcfg -j 2 -s
|
||||
|
||||
# bug hunting
|
||||
- name: "bug hunting"
|
||||
compiler: gcc
|
||||
script:
|
||||
- make clean
|
||||
- make USE_Z3=yes -j2 all
|
||||
- ./testrunner TestExprEngine
|
||||
- python3 test/bug-hunting/cve.py
|
||||
- git clone https://github.com/regehr/itc-benchmarks.git ~/itc
|
||||
- python3 test/bug-hunting/itc.py
|
||||
- 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
|
||||
- cd -
|
||||
- python3 test/bug-hunting/juliet.py
|
||||
|
||||
# check a lot of stuff that only needs to be checked in a single configuration
|
||||
- name: "misc"
|
||||
|
@ -117,9 +89,6 @@ matrix:
|
|||
# check --dump
|
||||
- ${CPPCHECK} test/testpreprocessor.cpp --dump
|
||||
- xmllint --noout test/testpreprocessor.cpp.dump
|
||||
# check python syntax by compiling all addon scripts
|
||||
- python -m compileall ./addons
|
||||
- python3 -m compileall ./addons
|
||||
# run pylint -- FIXME these are temporarily commented out because there is a syntax error in pylint
|
||||
# - pylint --rcfile=pylintrc_travis addons/*.py
|
||||
# - pylint --rcfile=pylintrc_travis htmlreport/cppcheck-htmlreport
|
||||
|
@ -171,20 +140,23 @@ matrix:
|
|||
- ${CPPCHECK} --dump namingng_test.c
|
||||
- python3 ../namingng.py --configfile ../naming.json --verify namingng_test.c.dump
|
||||
- cd ../..
|
||||
# try CMake
|
||||
- mkdir cmake.output
|
||||
- cd cmake.output
|
||||
- cmake -G "Unix Makefiles" -DBUILD_TESTS=On ..
|
||||
- make -s -j2 check
|
||||
- cd ..
|
||||
# -funsigned-char
|
||||
|
||||
# bug hunting
|
||||
- name: "bug hunting"
|
||||
compiler: gcc
|
||||
script:
|
||||
- make clean
|
||||
- make -s -j2 CXXFLAGS=-funsigned-char testrunner
|
||||
- ./testrunner TestSymbolDatabase
|
||||
# check .json files
|
||||
- find . -name '*.json' | xargs -n 1 python3 -m json.tool > /dev/null
|
||||
# build fuzz client
|
||||
- make -s -j2 CXXFLAGS="-fsanitize=address" -C oss-fuzz fuzz-client
|
||||
- make USE_Z3=yes -j2 all
|
||||
- ./testrunner TestExprEngine
|
||||
- python3 test/bug-hunting/cve.py
|
||||
- git clone https://github.com/regehr/itc-benchmarks.git ~/itc
|
||||
- python3 test/bug-hunting/itc.py
|
||||
- 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
|
||||
- cd -
|
||||
- python3 test/bug-hunting/juliet.py
|
||||
|
||||
# check if dmake needs to be rerun (this job may fail)
|
||||
- name: "rerun dmake?"
|
||||
|
@ -208,15 +180,6 @@ matrix:
|
|||
- CXX=g++ CXXFLAGS="${ORIGINAL_CXXFLAGS}" make cppcheck check -j 2 -s
|
||||
- make clean
|
||||
- CXX=clang++ CXXFLAGS="${ORIGINAL_CXXFLAGS}" make cppcheck check -j 2 -s
|
||||
# check if cppcheck builds on osx
|
||||
- name: "make osx"
|
||||
os: osx
|
||||
before_install:
|
||||
- true
|
||||
script:
|
||||
- CXX=g++ CXXFLAGS="${ORIGINAL_CXXFLAGS}" make cppcheck check -j 2 -s
|
||||
- make clean
|
||||
- CXX=clang++ CXXFLAGS="${ORIGINAL_CXXFLAGS}" make cppcheck check -j 2 -s
|
||||
|
||||
script:
|
||||
# fail the entire job as soon as one of the subcommands exits non-zero to save time and resources
|
||||
|
@ -233,40 +196,6 @@ script:
|
|||
# compile cppcheck, default build
|
||||
- echo $CXXFLAGS
|
||||
- make -s check -j2
|
||||
# compile gui
|
||||
- cd gui
|
||||
- qmake
|
||||
- echo $CXXFLAGS
|
||||
- make -s -j2
|
||||
# building gui generates some more files that cppcheck can check, so check the repo *after* building gui
|
||||
- cd ../
|
||||
# self check lib/cli
|
||||
- mkdir b1
|
||||
- ${CPPCHECK} -q -j2 --template=gcc --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/tinyxml/ -Icli --inconclusive --enable=style,performance,portability,warning,internal --exception-handling cli lib
|
||||
# check gui with qt settings
|
||||
- mkdir b2
|
||||
- ${CPPCHECK} -q -j2 --template=gcc --cppcheck-build-dir=b2 -D__CPPCHECK__ -DQT_VERSION=0x050000 --error-exitcode=1 --inline-suppr --suppressions-list=.travis_suppressions --library=qt --addon=naming.json -Ilib -Iexternals/simplecpp/ -Iexternals/tinyxml/ --enable=style,performance,portability,warning,internal --exception-handling gui/*.cpp
|
||||
# self check test and tools
|
||||
- ${CPPCHECK} -q -j2 --template=gcc -D__CPPCHECK__ --error-exitcode=1 --inline-suppr --suppressions-list=.travis_suppressions --library=cppcheck-lib -Ilib -Iexternals/simplecpp/ -Iexternals/tinyxml/ -Icli --inconclusive --enable=style,performance,portability,warning,internal --exception-handling test/*.cpp tools
|
||||
# Build gui
|
||||
- cd ./gui
|
||||
# clean rebuild
|
||||
- git clean -dfx .
|
||||
# can't set this as env flags, so try again with HAVE_RULES=yes
|
||||
- qmake HAVE_RULES=yes
|
||||
- echo $CXXFLAGS
|
||||
- make -s -j2
|
||||
- cd ../
|
||||
# Build gui/test
|
||||
- cd gui/test/projectfile
|
||||
- qmake && make -s -j2 && ./test-projectfile
|
||||
- cd -
|
||||
# Build triage
|
||||
- cd ./tools/triage
|
||||
- git clean -dfx .
|
||||
- qmake
|
||||
- make -s -j2
|
||||
- cd ../../
|
||||
# Testing cli
|
||||
- cp -R . ../cppcheck\ 2
|
||||
- cd ../cppcheck\ 2/test/cli # path with space
|
||||
|
|
8
Makefile
8
Makefile
|
@ -469,7 +469,7 @@ $(libcppdir)/checkpostfixoperator.o: lib/checkpostfixoperator.cpp lib/check.h li
|
|||
$(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/checkstl.h lib/config.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
|
||||
$(libcppdir)/checkstl.o: lib/checkstl.cpp lib/astutils.h lib/check.h lib/checknullpointer.h lib/checkstl.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
|
||||
|
@ -616,7 +616,7 @@ test/testcharvar.o: test/testcharvar.cpp lib/check.h lib/checkother.h lib/config
|
|||
test/testclangimport.o: test/testclangimport.cpp lib/clangimport.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/tinyxml/tinyxml2.h lib/check.h lib/checkclass.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/testclass.o: test/testclass.cpp externals/tinyxml/tinyxml2.h lib/check.h lib/checkclass.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/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
|
||||
|
@ -625,7 +625,7 @@ test/testcmdlineparser.o: test/testcmdlineparser.cpp cli/cmdlineparser.h lib/con
|
|||
test/testcondition.o: test/testcondition.cpp externals/simplecpp/simplecpp.h externals/tinyxml/tinyxml2.h lib/check.h lib/checkcondition.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/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/testconstructors.o: test/testconstructors.cpp lib/check.h lib/checkclass.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/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
|
||||
|
@ -757,7 +757,7 @@ test/testuninitvar.o: test/testuninitvar.cpp lib/check.h lib/checkuninitvar.h li
|
|||
test/testunusedfunctions.o: test/testunusedfunctions.cpp lib/check.h lib/checkunusedfunctions.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/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/testunusedprivfunc.o: test/testunusedprivfunc.cpp externals/simplecpp/simplecpp.h lib/check.h lib/checkclass.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/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
|
||||
|
|
|
@ -137,6 +137,7 @@ class Token:
|
|||
isUnsigned Is this token a unsigned type
|
||||
isSigned Is this token a signed type
|
||||
isExpandedMacro Is this token a expanded macro token
|
||||
isSplittedVarDecl Is this token a splitted variable declaration. "int a,b; => int a; int b;"
|
||||
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
|
||||
|
@ -185,6 +186,7 @@ class Token:
|
|||
isUnsigned = False
|
||||
isSigned = False
|
||||
isExpandedMacro = False
|
||||
isSplittedVarDecl = False
|
||||
varId = None
|
||||
variableId = None
|
||||
variable = None
|
||||
|
@ -245,6 +247,8 @@ class Token:
|
|||
self.isLogicalOp = True
|
||||
if element.get('isExpandedMacro'):
|
||||
self.isExpandedMacro = True
|
||||
if element.get('isSplittedVarDecl'):
|
||||
self.isSplittedVarDecl = True
|
||||
self.linkId = element.get('link')
|
||||
self.link = None
|
||||
if element.get('varId'):
|
||||
|
@ -275,10 +279,10 @@ class Token:
|
|||
attrs = ["Id", "str", "scopeId", "isName", "isUnsigned", "isSigned",
|
||||
"isNumber", "isInt", "isFloat", "isString", "strlen",
|
||||
"isChar", "isOp", "isArithmeticalOp", "isComparisonOp",
|
||||
"isLogicalOp", "isExpandedMacro", "linkId", "varId",
|
||||
"variableId", "functionId", "valuesId", "valueType",
|
||||
"typeScopeId", "astParentId", "astOperand1Id", "file",
|
||||
"linenr", "column"]
|
||||
"isLogicalOp", "isExpandedMacro", "isSplittedVarDecl",
|
||||
"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))
|
||||
|
|
111
addons/misra.py
111
addons/misra.py
|
@ -1638,92 +1638,33 @@ class MisraChecker:
|
|||
if maxval >= sz:
|
||||
self.reportError(token, 12, 2)
|
||||
|
||||
def misra_12_3(self, data, rawTokens, filename):
|
||||
# We need an additional check for closing braces using in
|
||||
# initialization lists and function calls, e.g.:
|
||||
# struct S a = {1, 2, 3}, b, c = foo(1, 2), d;
|
||||
# ^ ^
|
||||
end_tokens_map = defaultdict(set)
|
||||
|
||||
skip_to = None
|
||||
def misra_12_3(self, data):
|
||||
for token in data.tokenlist:
|
||||
if skip_to:
|
||||
if token == skip_to:
|
||||
skip_to = None
|
||||
else:
|
||||
continue
|
||||
# Skip tokens in function call body
|
||||
if token.function and token.next and token.next.str == "(":
|
||||
skip_to = token.next.link
|
||||
# Skip tokens in initializer lists
|
||||
if simpleMatch(token, '= {'):
|
||||
skip_to = token.next.link
|
||||
|
||||
if token.scope.type in ('Enum', 'Class', 'Struct', 'Global'):
|
||||
continue
|
||||
# Save last tokens from function calls and initializer lists in
|
||||
# initialization sequence
|
||||
if simpleMatch(token, ') ;') or simpleMatch(token, '} ;'):
|
||||
if (token.isExpandedMacro):
|
||||
end_tokens_map[token.next.linenr].add(token.next.column)
|
||||
else:
|
||||
end_tokens_map[token.linenr].add(token.column)
|
||||
if token.str != ',':
|
||||
continue
|
||||
if token.astParent:
|
||||
if token.astParent.str in ('(', '{'):
|
||||
end_token = token.astParent.link
|
||||
if end_token:
|
||||
end_tokens_map[end_token.linenr].add(end_token.column)
|
||||
continue
|
||||
elif token.astParent.str == ',':
|
||||
continue
|
||||
self.reportError(token, 12, 3)
|
||||
|
||||
# Cppcheck performs some simplifications in variables declaration code:
|
||||
# int a, b, c;
|
||||
# Will be reresented in dump file as:
|
||||
# int a; int b; int c;
|
||||
name_tokens_map = {}
|
||||
for v in data.variables:
|
||||
if v.isArgument:
|
||||
continue
|
||||
nt = v.nameToken
|
||||
if nt and nt.scope and nt.scope.type not in ('Enum', 'Class', 'Struct'):
|
||||
name_tokens_map.setdefault(nt.linenr, set())
|
||||
name_tokens_map[nt.linenr].add(nt.column)
|
||||
if not name_tokens_map:
|
||||
return
|
||||
|
||||
# Select tokens to check
|
||||
maybe_map = {}
|
||||
for linenr in set(name_tokens_map.keys()) | set(end_tokens_map.keys()):
|
||||
maybe_map[linenr] = name_tokens_map.get(linenr, set())
|
||||
maybe_map[linenr] |= end_tokens_map.get(linenr, set())
|
||||
|
||||
# Check variables declaration code in raw tokens to distinguish ';' and
|
||||
# ',' symbols.
|
||||
STATE_SKIP = 0
|
||||
STATE_CHECK = 1
|
||||
state = STATE_SKIP
|
||||
cur_line = min(maybe_map)
|
||||
for tok in rawTokens:
|
||||
if tok.linenr in maybe_map and tok.column in maybe_map[tok.linenr]:
|
||||
if tok.linenr in end_tokens_map and tok.column in end_tokens_map[tok.linenr]:
|
||||
if tok.str == ',' or (tok.next and tok.next.str == ','):
|
||||
self.reportError(tok, 12, 3)
|
||||
end_tokens_map[tok.linenr].remove(tok.column)
|
||||
state = STATE_CHECK
|
||||
cur_line = tok.linenr
|
||||
if tok.str in ('(', ';', '{'):
|
||||
state = STATE_SKIP
|
||||
if tok.linenr > cur_line:
|
||||
maybe_map.pop(cur_line)
|
||||
if not maybe_map:
|
||||
if token.str == ';' and (token.isSplittedVarDecl 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
|
||||
cur_line = min(maybe_map)
|
||||
if state == STATE_CHECK and tok.str == ',':
|
||||
self.reportError(tok, 12, 3)
|
||||
elif prev.str in ')}]':
|
||||
prev = prev.link
|
||||
elif prev.str in '({[':
|
||||
break
|
||||
prev = prev.previous
|
||||
|
||||
def misra_12_4(self, data):
|
||||
if typeBits['INT'] == 16:
|
||||
|
@ -2979,7 +2920,7 @@ class MisraChecker:
|
|||
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, data.rawTokens, filename)
|
||||
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)
|
||||
|
|
|
@ -393,14 +393,14 @@ 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; };
|
||||
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;
|
||||
typedef struct { int a; int b; int c, d; } misra_12_3_s3_t;
|
||||
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
|
||||
void misra_12_3_fn1(int, int); static int misra_12_3_v5, misra_12_4_v6; // 12.3
|
||||
void misra_12_3_fn2(int a, int b) // 2.7
|
||||
{ int d, e; } // 12.3
|
||||
|
@ -459,6 +459,8 @@ void misra_12_3(int a, int b, int c) { // no warning
|
|||
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
|
||||
{
|
||||
|
|
|
@ -20,6 +20,6 @@ C2::C2() : f(NULL) {}
|
|||
|
||||
static bool test_misra_21_1_crash()
|
||||
{
|
||||
auto misra_21_1_C a, b;
|
||||
auto misra_21_1_C a, b; // 12.3
|
||||
a = b;
|
||||
}
|
||||
|
|
32
cfg/std.cfg
32
cfg/std.cfg
|
@ -3859,8 +3859,28 @@ The obsolete function 'gets' is called. With 'gets' you'll get a buffer overrun
|
|||
</arg>
|
||||
</function>
|
||||
<!-- void * memcpy(void *ct, const void *cs, size_t n);-->
|
||||
<function name="memcpy,std::memcpy">
|
||||
<returnValue type="void *"/>
|
||||
<noreturn>false</noreturn>
|
||||
<leak-ignore/>
|
||||
<arg nr="1" direction="out">
|
||||
<not-null/>
|
||||
<minsize type="argvalue" arg="3"/>
|
||||
</arg>
|
||||
<arg nr="2" direction="in">
|
||||
<not-null/>
|
||||
<not-uninit/>
|
||||
<minsize type="argvalue" arg="3"/>
|
||||
</arg>
|
||||
<arg nr="3" direction="in">
|
||||
<not-uninit/>
|
||||
<not-bool/>
|
||||
<valid>0:</valid>
|
||||
</arg>
|
||||
</function>
|
||||
<!-- wchar_t * wmemcpy(wchar_t *ct, const wchar_t *cs, size_t n);-->
|
||||
<function name="memcpy,std::memcpy,wmemcpy,std::wmemcpy">
|
||||
<function name="wmemcpy,std::wmemcpy">
|
||||
<returnValue type="wchar_t *"/>
|
||||
<noreturn>false</noreturn>
|
||||
<leak-ignore/>
|
||||
<arg nr="1" direction="out">
|
||||
|
@ -7260,6 +7280,16 @@ initializer list (7) string& replace (const_iterator i1, const_iterator i2, init
|
|||
<not-uninit/>
|
||||
</arg>
|
||||
</function>
|
||||
<!-- template< class ForwardIt, class Generator > void std::generate (ForwardIt first, ForwardIt last, Generator g ); (until C++20) -->
|
||||
<!-- template< class ForwardIt, class Generator > constexpr void std::generate (ForwardIt first, ForwardIt last, Generator g ); (since C++20) -->
|
||||
<!-- template< class ExecutionPolicy, class ForwardIt, class Generator > void std::generate (ExecutionPolicy&& policy, ForwardIt first, ForwardIt last, Generator g ); (since C++17) -->
|
||||
<function name="std::generate">
|
||||
<noreturn>false</noreturn>
|
||||
<returnValue type="void"/>
|
||||
<arg nr="any">
|
||||
<not-uninit/>
|
||||
</arg>
|
||||
</function>
|
||||
<!-- template< class OutputIt, class Size, class T > void std::fill_n( OutputIt first, Size count, const T& value ); // until C++11 -->
|
||||
<!-- template< class OutputIt, class Size, class T > OutputIt std::fill_n( OutputIt first, Size count, const T& value ); // since C++11, until C++20 -->
|
||||
<!-- template< class OutputIt, class Size, class T > constexpr OutputIt std::fill_n( OutputIt first, Size count, const T& value ); // since C++20 -->
|
||||
|
|
|
@ -5176,6 +5176,20 @@
|
|||
<not-bool/>
|
||||
</arg>
|
||||
</function>
|
||||
<!-- void wxLogStatus(const char * format, ...) -->
|
||||
<!-- void wxLogSysError(const char * format, ...) -->
|
||||
<function name="wxLogStatus,wxLogSysError">
|
||||
<noreturn>false</noreturn>
|
||||
<returnValue type="void"/>
|
||||
<leak-ignore/>
|
||||
<formatstr/>
|
||||
<arg nr="1" direction="in">
|
||||
<formatstr/>
|
||||
<not-null/>
|
||||
<not-uninit/>
|
||||
<not-bool/>
|
||||
</arg>
|
||||
</function>
|
||||
<!--static wxString wxStandardPaths::MSWGetShellDir(int csidl)-->
|
||||
<function name="wxStandardPaths::MSWGetShellDir">
|
||||
<noreturn>false</noreturn>
|
||||
|
@ -14346,4 +14360,25 @@ wxItemKind kind = wxITEM_NORMAL) -->
|
|||
<valid>0:</valid>
|
||||
</arg>
|
||||
</function>
|
||||
<!-- wxPen* wxPenList::FindOrCreatePen(const wxColour & colour, int width = 1, wxPenStyle style = wxPENSTYLE_SOLID) -->
|
||||
<!-- wxPen* wxThePenList::FindOrCreatePen(const wxColour & colour, int width = 1, wxPenStyle style = wxPENSTYLE_SOLID) -->
|
||||
<function name="wxPenList::FindOrCreatePen,wxThePenList::FindOrCreatePen">
|
||||
<noreturn>false</noreturn>
|
||||
<use-retval/>
|
||||
<returnValue type="wxPen*"/>
|
||||
<arg nr="1" direction="in"/>
|
||||
<arg nr="2" default="1" direction="in">
|
||||
<not-uninit/>
|
||||
</arg>
|
||||
<arg nr="3" default="wxPENSTYLE_SOLID" direction="in"/>
|
||||
</function>
|
||||
<!-- wxBrush* wxBrushList::FindOrCreateBrush(const wxColour & colour, wxBrushStyle style = wxBRUSHSTYLE_SOLID) -->
|
||||
<!-- wxBrush* wxTheBrushList::FindOrCreateBrush(const wxColour & colour, wxBrushStyle style = wxBRUSHSTYLE_SOLID) -->
|
||||
<function name="wxBrushList::FindOrCreateBrush,wxTheBrushList::FindOrCreateBrush">
|
||||
<noreturn>false</noreturn>
|
||||
<use-retval/>
|
||||
<returnValue type="wxBrush*"/>
|
||||
<arg nr="1" direction="in"/>
|
||||
<arg nr="2" default="wxBRUSHSTYLE_SOLID" direction="in"/>
|
||||
</function>
|
||||
</def>
|
||||
|
|
|
@ -227,8 +227,12 @@ bool CmdLineParser::parseFromArgs(int argc, const char* const argv[])
|
|||
else if (std::strcmp(argv[i], "--check-library") == 0)
|
||||
mSettings->checkLibrary = true;
|
||||
|
||||
else if (std::strcmp(argv[i], "--clang") == 0)
|
||||
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));
|
||||
|
@ -972,11 +976,13 @@ void CmdLineParser::printHelp()
|
|||
" analysis is disabled by this flag.\n"
|
||||
" --check-library Show information messages when library files have\n"
|
||||
" incomplete info.\n"
|
||||
" --clang Use Clang parser instead of the builtin Cppcheck\n"
|
||||
" parser. Cppcheck executes `clang`. The Clang AST is\n"
|
||||
" imported and converted into Cppcheck data. After that\n"
|
||||
" the normal Cppcheck analysis is used. You must have\n"
|
||||
" `clang` in PATH.\n"
|
||||
" --clang=<path> 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=<dir>\n"
|
||||
" Path (prefix) to be excluded from configuration\n"
|
||||
" checking. Preprocessor configurations defined in\n"
|
||||
|
|
|
@ -445,7 +445,7 @@
|
|||
<item>
|
||||
<widget class="QRadioButton" name="mBtnClangParser">
|
||||
<property name="text">
|
||||
<string>Clang</string>
|
||||
<string>Clang (experimental)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
|
|
@ -299,6 +299,10 @@ static T* nextAfterAstRightmostLeafGeneric(T* tok)
|
|||
if (!rightmostLeaf || !rightmostLeaf->astOperand1())
|
||||
return nullptr;
|
||||
do {
|
||||
if (const Token* lam = findLambdaEndToken(rightmostLeaf)) {
|
||||
rightmostLeaf = lam;
|
||||
break;
|
||||
}
|
||||
if (rightmostLeaf->astOperand2())
|
||||
rightmostLeaf = rightmostLeaf->astOperand2();
|
||||
else
|
||||
|
@ -952,6 +956,21 @@ bool isOppositeCond(bool isNot, bool cpp, const Token * const cond1, const Token
|
|||
if (!cond1 || !cond2)
|
||||
return false;
|
||||
|
||||
if (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")
|
||||
|
|
|
@ -247,6 +247,21 @@ static void uninit(const Token *tok, const ExprEngine::Value &value, ExprEngine:
|
|||
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;
|
||||
|
|
|
@ -506,7 +506,7 @@ void CheckAutoVariables::checkVarLifetimeScope(const Token * start, const Token
|
|||
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)) {
|
||||
for (const LifetimeToken& lt : getLifetimeTokens(tok, true)) {
|
||||
const Variable* var = lt.token->variable();
|
||||
if (var && !var->isGlobal() && !var->isStatic() && !var->isReference() && !var->isRValueReference() &&
|
||||
isInScope(var->nameToken(), tok->scope())) {
|
||||
|
@ -532,10 +532,11 @@ void CheckAutoVariables::checkVarLifetimeScope(const Token * start, const Token
|
|||
for (const ValueFlow::Value& val:tok->values()) {
|
||||
if (!val.isLocalLifetimeValue() && !val.isSubFunctionLifetimeValue())
|
||||
continue;
|
||||
for (const LifetimeToken& lt :getLifetimeTokens(getParentLifetime(val.tokvalue))) {
|
||||
const bool escape = Token::Match(tok->astParent(), "return|throw");
|
||||
for (const LifetimeToken& lt : getLifetimeTokens(getParentLifetime(val.tokvalue), escape)) {
|
||||
const Token * tokvalue = lt.token;
|
||||
if (val.isLocalLifetimeValue()) {
|
||||
if (Token::Match(tok->astParent(), "return|throw")) {
|
||||
if (escape) {
|
||||
if (getPointerDepth(tok) < getPointerDepth(tokvalue))
|
||||
continue;
|
||||
if (!isLifetimeBorrowed(tok, mSettings))
|
||||
|
|
|
@ -1608,14 +1608,11 @@ void CheckClass::virtualDestructor()
|
|||
if (scope->definedType->derivedFrom.empty()) {
|
||||
if (printInconclusive) {
|
||||
const Function *destructor = scope->getDestructor();
|
||||
if (destructor) {
|
||||
if (!((destructor->hasVirtualSpecifier() && destructor->access == AccessControl::Public) ||
|
||||
(destructor->access == AccessControl::Protected))) {
|
||||
for (const Function &func : scope->functionList) {
|
||||
if (func.hasVirtualSpecifier()) {
|
||||
inconclusiveErrors.push_back(destructor);
|
||||
break;
|
||||
}
|
||||
if (destructor && !destructor->hasVirtualSpecifier() && destructor->access == AccessControl::Public) {
|
||||
for (const Function &func : scope->functionList) {
|
||||
if (func.hasVirtualSpecifier()) {
|
||||
inconclusiveErrors.push_back(destructor);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -701,8 +701,10 @@ void CheckCondition::multiCondition2()
|
|||
if (!firstCondition)
|
||||
return ChildrenToVisit::none;
|
||||
if (firstCondition->str() == "&&") {
|
||||
return ChildrenToVisit::op1_and_op2;
|
||||
} else if (!firstCondition->hasKnownIntValue()) {
|
||||
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);
|
||||
|
|
|
@ -1412,9 +1412,15 @@ void CheckOther::checkConstVariable()
|
|||
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)
|
||||
if (!isConst) {
|
||||
castToNonConst = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (castToNonConst)
|
||||
|
|
|
@ -2547,7 +2547,6 @@ void CheckStl::knownEmptyContainer()
|
|||
if (tok->str() == "for") {
|
||||
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;
|
||||
|
|
|
@ -1095,16 +1095,21 @@ void clangimport::AstNode::createTokensFunctionDecl(TokenList *tokenList)
|
|||
symbolDatabase->scopeList.push_back(Scope(nullptr, nullptr, nestedIn));
|
||||
scope = &symbolDatabase->scopeList.back();
|
||||
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];
|
||||
|
|
|
@ -328,21 +328,17 @@ unsigned int CppCheck::check(const std::string &path)
|
|||
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";
|
||||
std::string exe = mSettings.clangExecutable;
|
||||
#ifdef _WIN32
|
||||
const std::string exe = "clang.exe";
|
||||
#else
|
||||
const std::string exe = "clang";
|
||||
// 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)) {
|
||||
if (mSettings.standards.cpp == Standards::CPP14)
|
||||
flags += "-std=c++14 ";
|
||||
else if (mSettings.standards.cpp == Standards::CPP17)
|
||||
flags += "-std=c++17 ";
|
||||
else if (mSettings.standards.cpp == Standards::CPP20)
|
||||
flags += "-std=c++20 ";
|
||||
}
|
||||
if (Path::isCPP(path) && !mSettings.standards.stdValue.empty())
|
||||
flags += "-std=" + mSettings.standards.stdValue + " ";
|
||||
|
||||
for (const std::string &i: mSettings.includePaths)
|
||||
flags += "-I" + i + " ";
|
||||
|
@ -413,12 +409,10 @@ unsigned int CppCheck::check(const ImportProject::FileSettings &fs)
|
|||
temp.mSettings.userDefines += fs.cppcheckDefines();
|
||||
temp.mSettings.includePaths = fs.includePaths;
|
||||
temp.mSettings.userUndefs = fs.undefs;
|
||||
if (fs.standard == "c++14")
|
||||
temp.mSettings.standards.cpp = Standards::CPP14;
|
||||
else if (fs.standard == "c++17")
|
||||
temp.mSettings.standards.cpp = Standards::CPP17;
|
||||
else if (fs.standard == "c++20")
|
||||
temp.mSettings.standards.cpp = Standards::CPP20;
|
||||
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) {
|
||||
|
|
|
@ -1910,6 +1910,8 @@ static ExprEngine::ValuePtr executeFunctionCall(const Token *tok, Data &data)
|
|||
|
||||
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();
|
||||
|
|
|
@ -162,7 +162,7 @@ void ImportProject::FileSettings::setIncludePaths(const std::string &basepath, c
|
|||
continue;
|
||||
if (it.compare(0,2,"%(")==0)
|
||||
continue;
|
||||
std::string s(Path::fromNativeSeparators(it));
|
||||
std::string s(Path::removeQuotationMarks(Path::fromNativeSeparators(it)));
|
||||
if (s[0] == '/' || (s.size() > 1U && s.compare(1,2,":/") == 0)) {
|
||||
if (!endsWith(s,'/'))
|
||||
s += '/';
|
||||
|
@ -217,13 +217,13 @@ ImportProject::Type ImportProject::import(const std::string &filename, Settings
|
|||
return ImportProject::Type::UNKNOWN;
|
||||
}
|
||||
|
||||
static std::string readUntil(const std::string &command, std::string::size_type *pos, const char until[])
|
||||
static std::string readUntil(const std::string& command, std::string::size_type* pos, const char until[], bool skipBackSlash = true)
|
||||
{
|
||||
std::string ret;
|
||||
bool str = false;
|
||||
while (*pos < command.size() && (str || !std::strchr(until, command[*pos]))) {
|
||||
if (command[*pos] == '\\')
|
||||
++*pos;
|
||||
if (skipBackSlash && command[*pos] == '\\')
|
||||
++* pos;
|
||||
if (*pos < command.size())
|
||||
ret += command[(*pos)++];
|
||||
if (endsWith(ret, '\"'))
|
||||
|
@ -232,112 +232,126 @@ static std::string readUntil(const std::string &command, std::string::size_type
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void skip_whitespaces(const std::string& command, std::string::size_type *pos)
|
||||
{
|
||||
while (*pos < command.size() && command[*pos] == ' ')
|
||||
(*pos)++;
|
||||
}
|
||||
|
||||
|
||||
static std::string parseTillNextCommandOpt(const std::string& singleCharOpts, const std::string& command, std::string::size_type *pos)
|
||||
{
|
||||
std::string ret;
|
||||
*pos = command.find_first_not_of("/-", *pos);
|
||||
|
||||
if (*pos >= command.size())
|
||||
return ret;
|
||||
|
||||
const char F = command[*pos];
|
||||
if (std::strchr(singleCharOpts.c_str(), F)) {
|
||||
(*pos)++;
|
||||
return std::string{F};
|
||||
}
|
||||
|
||||
ret = readUntil(command, pos, " =");
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ImportProject::FileSettings::parseCommandStd(const std::string& command, std::string::size_type *pos, std::string& defs)
|
||||
{
|
||||
std::string def{};
|
||||
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
|
||||
} else {
|
||||
def += "__cplusplus=";
|
||||
def += stddef;
|
||||
def += ";";
|
||||
}
|
||||
}
|
||||
defs += def;
|
||||
}
|
||||
|
||||
void ImportProject::FileSettings::parseCommandDefine(const std::string& command, std::string::size_type *pos, std::string& defs)
|
||||
{
|
||||
const bool skipBackSlash = false;
|
||||
defs += readUntil(command, pos, " =");
|
||||
const std::string defval = readUntil(command, pos, " ", skipBackSlash);
|
||||
if (!defval.empty())
|
||||
defs += defval;
|
||||
defs += ';';
|
||||
}
|
||||
|
||||
void ImportProject::FileSettings::parseCommandUndefine(const std::string& command, std::string::size_type *pos)
|
||||
{
|
||||
const std::string fval = readUntil(command, pos, " ");
|
||||
undefs.insert(fval);
|
||||
}
|
||||
|
||||
void ImportProject::FileSettings::parseCommandInclude(const std::string& command, std::string::size_type *pos)
|
||||
{
|
||||
const bool skipBackSlash = false;
|
||||
const std::string fval = readUntil(command, pos, " ", skipBackSlash);
|
||||
if (std::find(includePaths.begin(), includePaths.end(), fval) == includePaths.end())
|
||||
includePaths.push_back(fval);
|
||||
}
|
||||
|
||||
void ImportProject::FileSettings::parseCommandSystemInclude(const std::string& command, std::string::size_type *pos)
|
||||
{
|
||||
const bool skipBackSlash = false;
|
||||
const std::string isystem = Path::removeQuotationMarks(readUntil(command, pos, " ", skipBackSlash));
|
||||
systemIncludePaths.push_back(isystem);
|
||||
}
|
||||
|
||||
void ImportProject::FileSettings::parseCommand(const std::string &command)
|
||||
{
|
||||
const std::string singleCharCommandOpts = "DUI";
|
||||
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++;
|
||||
skip_whitespaces(command, &pos);
|
||||
if (pos >= command.size())
|
||||
break;
|
||||
if (command[pos] != '/' && command[pos] != '-')
|
||||
continue;
|
||||
pos++;
|
||||
|
||||
const auto opt = parseTillNextCommandOpt(singleCharCommandOpts, command, &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') {
|
||||
const std::string defval = readUntil(command, &pos, " ");
|
||||
defs += fval;
|
||||
if (!defval.empty())
|
||||
defs += defval;
|
||||
defs += ';';
|
||||
} else if (F=='U')
|
||||
undefs.insert(fval);
|
||||
else if (F=='I') {
|
||||
if (std::find(includePaths.begin(), includePaths.end(), fval) == includePaths.end())
|
||||
includePaths.push_back(fval);
|
||||
} 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 += ";";
|
||||
}
|
||||
}
|
||||
if (opt=="D")
|
||||
parseCommandDefine(command, &pos, defs);
|
||||
else if (opt=="U")
|
||||
parseCommandUndefine(command, &pos);
|
||||
else if (opt=="I")
|
||||
parseCommandInclude(command, &pos);
|
||||
else if (opt=="isystem")
|
||||
parseCommandSystemInclude(command, &pos);
|
||||
else if (opt=="std")
|
||||
parseCommandStd(command, &pos, defs);
|
||||
else if (opt=="municode")
|
||||
defs += "UNICODE;";
|
||||
else if (opt=="fpic")
|
||||
defs += "__pic__;";
|
||||
else if (opt=="fPIC")
|
||||
defs += "__PIC__;";
|
||||
else if (opt=="fpie")
|
||||
defs += "__pie__;";
|
||||
else if (opt=="fPIE")
|
||||
defs += "__PIE__;";
|
||||
}
|
||||
setDefines(defs);
|
||||
}
|
||||
|
|
|
@ -77,6 +77,12 @@ public:
|
|||
bool useMfc;
|
||||
|
||||
void parseCommand(const std::string &command);
|
||||
|
||||
void parseCommandStd(const std::string& command, std::string::size_type *pos, std::string& defs);
|
||||
void parseCommandDefine(const std::string& command, std::string::size_type *pos, std::string& defs);
|
||||
void parseCommandUndefine(const std::string& command, std::string::size_type *pos);
|
||||
void parseCommandInclude(const std::string& command, std::string::size_type *pos);
|
||||
void parseCommandSystemInclude(const std::string& command, std::string::size_type *pos);
|
||||
void setDefines(std::string defs);
|
||||
void setIncludePaths(const std::string &basepath, const std::list<std::string> &in, std::map<std::string, std::string, cppcheck::stricmp> &variables);
|
||||
};
|
||||
|
|
|
@ -37,6 +37,7 @@ Settings::Settings()
|
|||
checkLibrary(false),
|
||||
checkUnusedTemplates(false),
|
||||
clang(false),
|
||||
clangExecutable("clang"),
|
||||
clangTidy(false),
|
||||
daca(false),
|
||||
debugBugHunting(false),
|
||||
|
|
|
@ -115,6 +115,9 @@ public:
|
|||
/** Use Clang */
|
||||
bool clang;
|
||||
|
||||
/** Custom Clang executable */
|
||||
std::string clangExecutable;
|
||||
|
||||
/** Use clang-tidy */
|
||||
bool clangTidy;
|
||||
|
||||
|
|
|
@ -38,10 +38,14 @@ struct Standards {
|
|||
/** 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;
|
||||
|
@ -68,6 +72,7 @@ struct Standards {
|
|||
return "";
|
||||
}
|
||||
bool setCPP(const std::string& str) {
|
||||
stdValue = str;
|
||||
if (str == "c++03" || str == "C++03") {
|
||||
cpp = CPP03;
|
||||
return true;
|
||||
|
@ -90,7 +95,7 @@ struct Standards {
|
|||
}
|
||||
return false;
|
||||
}
|
||||
const std::string getCPP() const {
|
||||
std::string getCPP() const {
|
||||
switch (cpp) {
|
||||
case CPP03:
|
||||
return "c++03";
|
||||
|
|
|
@ -1939,7 +1939,7 @@ void Variable::evaluate(const Settings* settings)
|
|||
setFlag(fIsReference, true); // Set also fIsReference
|
||||
}
|
||||
|
||||
if (tok->isMaybeUnused()) {
|
||||
if (tok->isAttributeMaybeUnused()) {
|
||||
setFlag(fIsMaybeUnused, true);
|
||||
}
|
||||
|
||||
|
@ -5355,12 +5355,15 @@ void SymbolDatabase::setValueType(Token *tok, const ValueType &valuetype)
|
|||
|
||||
if (vt1 && Token::Match(parent, "<<|>>")) {
|
||||
if (!mIsCpp || (vt2 && vt2->isIntegral())) {
|
||||
if (vt1->type < ValueType::Type::BOOL || vt1->type >= ValueType::Type::INT)
|
||||
setValueType(parent, *vt1);
|
||||
else {
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -769,7 +769,15 @@ void TemplateSimplifier::addInstantiation(Token *token, const std::string &scope
|
|||
{
|
||||
simplifyTemplateArgs(token->tokAt(2), token->next()->findClosingBracket());
|
||||
|
||||
mTemplateInstantiations.emplace_back(token, scope);
|
||||
TokenAndName instantiation(token, scope);
|
||||
|
||||
// check if instantiation already exists before adding it
|
||||
std::list<TokenAndName>::iterator it = std::find(mTemplateInstantiations.begin(),
|
||||
mTemplateInstantiations.end(),
|
||||
instantiation);
|
||||
|
||||
if (it == mTemplateInstantiations.end())
|
||||
mTemplateInstantiations.emplace_back(instantiation);
|
||||
}
|
||||
|
||||
void TemplateSimplifier::getTemplateInstantiations()
|
||||
|
@ -835,9 +843,7 @@ void TemplateSimplifier::getTemplateInstantiations()
|
|||
std::string qualification;
|
||||
Token * qualificationTok = tok;
|
||||
while (Token::Match(tok, "%name% :: %name%")) {
|
||||
// ignore redundant namespaces
|
||||
if (scopeName.find(tok->str()) == std::string::npos)
|
||||
qualification += (qualification.empty() ? "" : " :: ") + tok->str();
|
||||
qualification += (qualification.empty() ? "" : " :: ") + tok->str();
|
||||
tok = tok->tokAt(2);
|
||||
}
|
||||
|
||||
|
|
|
@ -1457,7 +1457,23 @@ static std::string stringFromTokenRange(const Token* start, const Token* end)
|
|||
ret << "unsigned ";
|
||||
if (tok->isLong() && !tok->isLiteral())
|
||||
ret << "long ";
|
||||
if (tok->originalName().empty() || tok->isUnsigned() || tok->isLong()) {
|
||||
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();
|
||||
|
|
61
lib/token.h
61
lib/token.h
|
@ -549,11 +549,11 @@ public:
|
|||
void isAttributeNodiscard(const bool value) {
|
||||
setFlag(fIsAttributeNodiscard, value);
|
||||
}
|
||||
bool isMaybeUnused() const {
|
||||
return getFlag(fIsMaybeUnused);
|
||||
bool isAttributeMaybeUnused() const {
|
||||
return getFlag(fIsAttributeMaybeUnused);
|
||||
}
|
||||
void isMaybeUnused(const bool value) {
|
||||
setFlag(fIsMaybeUnused, value);
|
||||
void isAttributeMaybeUnused(const bool value) {
|
||||
setFlag(fIsAttributeMaybeUnused, value);
|
||||
}
|
||||
void setCppcheckAttribute(TokenImpl::CppcheckAttributes::Type type, MathLib::bigint value) {
|
||||
mImpl->setCppcheckAttribute(type, value);
|
||||
|
@ -612,6 +612,12 @@ public:
|
|||
setFlag(fExternC, b);
|
||||
}
|
||||
|
||||
bool isSplittedVarDecl() const {
|
||||
return getFlag(fIsSplitVarDecl);
|
||||
}
|
||||
void isSplittedVarDecl(bool b) {
|
||||
setFlag(fIsSplitVarDecl, b);
|
||||
}
|
||||
|
||||
bool isBitfield() const {
|
||||
return mImpl->mBits > 0;
|
||||
|
@ -1182,19 +1188,20 @@ private:
|
|||
fIsAttributeNothrow = (1 << 13), // __attribute__((nothrow)), __declspec(nothrow)
|
||||
fIsAttributeUsed = (1 << 14), // __attribute__((used))
|
||||
fIsAttributePacked = (1 << 15), // __attribute__((packed))
|
||||
fIsControlFlowKeyword = (1 << 16), // if/switch/while/...
|
||||
fIsOperatorKeyword = (1 << 17), // operator=, etc
|
||||
fIsComplex = (1 << 18), // complex/_Complex type
|
||||
fIsEnumType = (1 << 19), // enumeration type
|
||||
fIsName = (1 << 20),
|
||||
fIsLiteral = (1 << 21),
|
||||
fIsTemplateArg = (1 << 22),
|
||||
fIsAttributeNodiscard = (1 << 23), // __attribute__ ((warn_unused_result)), [[nodiscard]]
|
||||
fAtAddress = (1 << 24), // @ 0x4000
|
||||
fIncompleteVar = (1 << 25),
|
||||
fConstexpr = (1 << 26),
|
||||
fExternC = (1 << 27),
|
||||
fIsMaybeUnused = (1 << 28), // [[maybe_unsed]]
|
||||
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),
|
||||
fIsSplitVarDecl = (1 << 29), // int a,b; <-- vardecl is split up
|
||||
};
|
||||
|
||||
Token::Type mTokType;
|
||||
|
@ -1256,6 +1263,26 @@ public:
|
|||
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)
|
||||
|
|
101
lib/tokenize.cpp
101
lib/tokenize.cpp
|
@ -4723,6 +4723,8 @@ bool Tokenizer::simplifyTokenList1(const char FileName[])
|
|||
|
||||
elseif();
|
||||
|
||||
simplifyOverloadedOperators();
|
||||
|
||||
validate();
|
||||
|
||||
list.front()->assignIndexes();
|
||||
|
@ -4950,9 +4952,9 @@ void Tokenizer::dump(std::ostream &out) const
|
|||
} else if (tok->isNumber()) {
|
||||
out << " type=\"number\"";
|
||||
if (MathLib::isInt(tok->str()))
|
||||
out << " isInt=\"True\"";
|
||||
out << " isInt=\"true\"";
|
||||
if (MathLib::isFloat(tok->str()))
|
||||
out << " isFloat=\"True\"";
|
||||
out << " isFloat=\"true\"";
|
||||
} else if (tok->tokType() == Token::eString)
|
||||
out << " type=\"string\" strlen=\"" << Token::getStrLength(tok) << '\"';
|
||||
else if (tok->tokType() == Token::eChar)
|
||||
|
@ -4962,16 +4964,18 @@ void Tokenizer::dump(std::ostream &out) const
|
|||
else if (tok->isOp()) {
|
||||
out << " type=\"op\"";
|
||||
if (tok->isArithmeticalOp())
|
||||
out << " isArithmeticalOp=\"True\"";
|
||||
out << " isArithmeticalOp=\"true\"";
|
||||
else if (tok->isAssignmentOp())
|
||||
out << " isAssignmentOp=\"True\"";
|
||||
out << " isAssignmentOp=\"true\"";
|
||||
else if (tok->isComparisonOp())
|
||||
out << " isComparisonOp=\"True\"";
|
||||
out << " isComparisonOp=\"true\"";
|
||||
else if (tok->tokType() == Token::eLogicalOp)
|
||||
out << " isLogicalOp=\"True\"";
|
||||
out << " isLogicalOp=\"true\"";
|
||||
}
|
||||
if (tok->isExpandedMacro())
|
||||
out << " isExpandedMacro=\"True\"";
|
||||
out << " isExpandedMacro=\"true\"";
|
||||
if (tok->isSplittedVarDecl())
|
||||
out << " isSplittedVarDecl=\"true\"";
|
||||
if (tok->link())
|
||||
out << " link=\"" << tok->link() << '\"';
|
||||
if (tok->varId() > 0)
|
||||
|
@ -6855,6 +6859,7 @@ void Tokenizer::simplifyVarDecl(Token * tokBegin, const Token * const tokEnd, co
|
|||
|
||||
if (tok2->str() == ",") {
|
||||
tok2->str(";");
|
||||
tok2->isSplittedVarDecl(true);
|
||||
//TODO: should we have to add also template '<>' links?
|
||||
TokenList::insertTokens(tok2, type0, typelen);
|
||||
}
|
||||
|
@ -6882,6 +6887,7 @@ void Tokenizer::simplifyVarDecl(Token * tokBegin, const Token * const tokEnd, co
|
|||
// "= x, " => "= x; type "
|
||||
if (tok2->str() == ",") {
|
||||
tok2->str(";");
|
||||
tok2->isSplittedVarDecl(true);
|
||||
TokenList::insertTokens(tok2, type0, typelen);
|
||||
}
|
||||
break;
|
||||
|
@ -9650,7 +9656,9 @@ void Tokenizer::findGarbageCode() const
|
|||
syntaxError(tok);
|
||||
if (Token::simpleMatch(tok, ",") &&
|
||||
!Token::Match(tok->tokAt(-2), "[ = , &|%name%")) {
|
||||
if (Token::Match(tok->previous(), "(|[|{|<|%assign%|%or%|%oror%|==|!=|+|-|/|!|>=|<=|~|^|::|sizeof|throw|decltype|typeof"))
|
||||
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);
|
||||
|
@ -10268,7 +10276,7 @@ void Tokenizer::simplifyCPPAttribute()
|
|||
Token* head = tok->tokAt(5);
|
||||
while (isCPPAttribute(head))
|
||||
head = head->tokAt(5);
|
||||
head->isMaybeUnused(true);
|
||||
head->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() == ":")
|
||||
|
@ -11206,6 +11214,81 @@ void Tokenizer::simplifyOperatorName()
|
|||
}
|
||||
}
|
||||
|
||||
void Tokenizer::simplifyOverloadedOperators()
|
||||
{
|
||||
if (isC())
|
||||
return;
|
||||
std::set<std::string> classNames;
|
||||
std::set<nonneg int> 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()
|
||||
{
|
||||
|
|
|
@ -743,6 +743,9 @@ private:
|
|||
*/
|
||||
void simplifyOperatorName();
|
||||
|
||||
/** simplify overloaded operators: 'obj(123)' => 'obj . operator() ( 123 )' */
|
||||
void simplifyOverloadedOperators();
|
||||
|
||||
/**
|
||||
* Remove [[attribute]] (C++11 and later) from TokenList
|
||||
*/
|
||||
|
|
|
@ -911,6 +911,16 @@ static void compilePrecedence2(Token *&tok, AST_state& state)
|
|||
// - 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();
|
||||
|
@ -1292,23 +1302,36 @@ static Token * createAstAtToken(Token *tok, bool cpp);
|
|||
static void createAstAtTokenInner(Token * const tok1, const Token *endToken, bool cpp)
|
||||
{
|
||||
for (Token *tok = tok1; tok && tok != endToken; tok = tok ? tok->next() : nullptr) {
|
||||
if (tok->str() == "{" && !iscpp11init(tok) && !tok->astOperand1()) {
|
||||
if (tok->str() == "{" && !iscpp11init(tok)) {
|
||||
if (Token::simpleMatch(tok->astOperand1(), ","))
|
||||
continue;
|
||||
if (Token::simpleMatch(tok->previous(), "( {"))
|
||||
;
|
||||
// struct assignment
|
||||
else if (Token::simpleMatch(tok->previous(), ") {") && Token::simpleMatch(tok->linkAt(-1), "( struct"))
|
||||
continue;
|
||||
// Lambda function
|
||||
else if (Token::simpleMatch(tok->astParent(), "(") &&
|
||||
Token::simpleMatch(tok->astParent()->astParent(), "[") &&
|
||||
tok->astParent()->astParent()->astOperand1() &&
|
||||
tok == tok->astParent()->astParent()->astOperand1()->astOperand1())
|
||||
;
|
||||
else {
|
||||
// function argument is initializer list?
|
||||
const Token *parent = tok->astParent();
|
||||
while (Token::simpleMatch(parent, ","))
|
||||
parent = parent->astParent();
|
||||
if (!parent || !Token::Match(parent->previous(), "%name% ("))
|
||||
// not function argument..
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Token::simpleMatch(tok->previous(), "( { ."))
|
||||
break;
|
||||
|
||||
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);
|
||||
}
|
||||
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();
|
||||
|
|
|
@ -488,7 +488,7 @@ static void setTokenValue(Token* tok, const ValueFlow::Value &value, const Setti
|
|||
|
||||
// cast..
|
||||
if (const Token *castType = getCastTypeStartToken(parent)) {
|
||||
if (astIsPointer(tok) && value.valueType == ValueFlow::Value::INT &&
|
||||
if (((tok->valueType() == nullptr && value.isImpossible()) || astIsPointer(tok)) && value.valueType == ValueFlow::Value::INT &&
|
||||
Token::simpleMatch(parent->astOperand1(), "dynamic_cast"))
|
||||
return;
|
||||
const ValueType &valueType = ValueType::parseDecl(castType, settings);
|
||||
|
@ -617,6 +617,10 @@ static void setTokenValue(Token* tok, const ValueFlow::Value &value, const Setti
|
|||
result.valueType = ValueFlow::Value::FLOAT;
|
||||
result.floatValue = floatValue1 - floatValue2;
|
||||
} else {
|
||||
// Avoid overflow
|
||||
if (value1.intvalue < 0 && value2.intvalue > value1.intvalue - LLONG_MIN)
|
||||
break;
|
||||
|
||||
result.intvalue = value1.intvalue - value2.intvalue;
|
||||
}
|
||||
// If the bound comes from the second value then invert the bound
|
||||
|
@ -781,9 +785,12 @@ static void setTokenValue(Token* tok, const ValueFlow::Value &value, const Setti
|
|||
if (!val.isIntValue() && !val.isFloatValue())
|
||||
continue;
|
||||
ValueFlow::Value v(val);
|
||||
if (v.isIntValue())
|
||||
if (v.isIntValue()) {
|
||||
if (v.intvalue == LLONG_MIN)
|
||||
// Value can't be inverted
|
||||
continue;
|
||||
v.intvalue = -v.intvalue;
|
||||
else
|
||||
} else
|
||||
v.floatValue = -v.floatValue;
|
||||
v.invertBound();
|
||||
setTokenValue(parent, v, settings);
|
||||
|
@ -2995,7 +3002,7 @@ ValueFlow::Value getLifetimeObjValue(const Token *tok, bool inconclusive)
|
|||
return result;
|
||||
}
|
||||
|
||||
std::vector<LifetimeToken> getLifetimeTokens(const Token* tok, ValueFlow::Value::ErrorPath errorPath, int depth)
|
||||
std::vector<LifetimeToken> getLifetimeTokens(const Token* tok, bool escape, ValueFlow::Value::ErrorPath errorPath, int depth)
|
||||
{
|
||||
if (!tok)
|
||||
return std::vector<LifetimeToken> {};
|
||||
|
@ -3012,10 +3019,10 @@ std::vector<LifetimeToken> getLifetimeTokens(const Token* tok, ValueFlow::Value:
|
|||
} else if (Token::simpleMatch(var->declEndToken(), "=")) {
|
||||
errorPath.emplace_back(var->declEndToken(), "Assigned to reference.");
|
||||
const Token *vartok = var->declEndToken()->astOperand2();
|
||||
if (vartok == tok || (var->isConst() && isTemporary(true, vartok, nullptr, true)))
|
||||
if (vartok == tok || (!escape && var->isConst() && isTemporary(true, vartok, nullptr, true)))
|
||||
return {{tok, true, std::move(errorPath)}};
|
||||
if (vartok)
|
||||
return getLifetimeTokens(vartok, std::move(errorPath), depth - 1);
|
||||
return getLifetimeTokens(vartok, escape, std::move(errorPath), depth - 1);
|
||||
} else if (Token::simpleMatch(var->nameToken()->astParent(), ":") &&
|
||||
var->nameToken()->astParent()->astParent() &&
|
||||
Token::simpleMatch(var->nameToken()->astParent()->astParent()->previous(), "for (")) {
|
||||
|
@ -3025,7 +3032,7 @@ std::vector<LifetimeToken> getLifetimeTokens(const Token* tok, ValueFlow::Value:
|
|||
return {{tok, true, std::move(errorPath)}};
|
||||
const Token* contok = var->nameToken()->astParent()->astOperand2();
|
||||
if (contok)
|
||||
return getLifetimeTokens(contok, std::move(errorPath), depth - 1);
|
||||
return getLifetimeTokens(contok, escape, std::move(errorPath), depth - 1);
|
||||
} else {
|
||||
return std::vector<LifetimeToken> {};
|
||||
}
|
||||
|
@ -3040,7 +3047,7 @@ std::vector<LifetimeToken> getLifetimeTokens(const Token* tok, ValueFlow::Value:
|
|||
for (const Token* returnTok : returns) {
|
||||
if (returnTok == tok)
|
||||
continue;
|
||||
for (LifetimeToken& lt : getLifetimeTokens(returnTok, std::move(errorPath), depth - 1)) {
|
||||
for (LifetimeToken& lt : getLifetimeTokens(returnTok, escape, std::move(errorPath), depth - 1)) {
|
||||
const Token* argvarTok = lt.token;
|
||||
const Variable* argvar = argvarTok->variable();
|
||||
if (!argvar)
|
||||
|
@ -3057,7 +3064,7 @@ std::vector<LifetimeToken> getLifetimeTokens(const Token* tok, ValueFlow::Value:
|
|||
lt.errorPath.emplace_back(returnTok, "Return reference.");
|
||||
lt.errorPath.emplace_back(tok->previous(), "Called function passing '" + argTok->str() + "'.");
|
||||
std::vector<LifetimeToken> arglts = LifetimeToken::setInconclusive(
|
||||
getLifetimeTokens(argTok, std::move(lt.errorPath), depth - 1), returns.size() > 1);
|
||||
getLifetimeTokens(argTok, escape, std::move(lt.errorPath), depth - 1), returns.size() > 1);
|
||||
result.insert(result.end(), arglts.begin(), arglts.end());
|
||||
}
|
||||
}
|
||||
|
@ -3069,7 +3076,7 @@ std::vector<LifetimeToken> getLifetimeTokens(const Token* tok, ValueFlow::Value:
|
|||
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(), std::move(errorPath), depth - 1), false);
|
||||
getLifetimeTokens(tok->tokAt(-2)->astOperand1(), escape, std::move(errorPath), depth - 1), false);
|
||||
}
|
||||
}
|
||||
} else if (Token::Match(tok, ".|::|[")) {
|
||||
|
@ -3092,10 +3099,10 @@ std::vector<LifetimeToken> getLifetimeTokens(const Token* tok, ValueFlow::Value:
|
|||
if (!v.isLocalLifetimeValue())
|
||||
continue;
|
||||
errorPath.insert(errorPath.end(), v.errorPath.begin(), v.errorPath.end());
|
||||
return getLifetimeTokens(v.tokvalue, std::move(errorPath));
|
||||
return getLifetimeTokens(v.tokvalue, escape, std::move(errorPath));
|
||||
}
|
||||
} else {
|
||||
return LifetimeToken::setAddressOf(getLifetimeTokens(vartok, std::move(errorPath)),
|
||||
return LifetimeToken::setAddressOf(getLifetimeTokens(vartok, escape, std::move(errorPath)),
|
||||
!(astIsContainer(vartok) && Token::simpleMatch(vartok->astParent(), "[")));
|
||||
}
|
||||
}
|
||||
|
@ -3706,8 +3713,12 @@ static void valueFlowLifetimeConstructor(Token* tok, TokenList* tokenlist, Error
|
|||
}
|
||||
|
||||
struct Lambda {
|
||||
enum class Capture {
|
||||
ByValue,
|
||||
ByReference
|
||||
};
|
||||
explicit Lambda(const Token * tok)
|
||||
: capture(nullptr), arguments(nullptr), returnTok(nullptr), bodyTok(nullptr) {
|
||||
: capture(nullptr), arguments(nullptr), returnTok(nullptr), bodyTok(nullptr), explicitCaptures() {
|
||||
if (!Token::simpleMatch(tok, "[") || !tok->link())
|
||||
return;
|
||||
capture = tok;
|
||||
|
@ -3722,12 +3733,31 @@ struct Lambda {
|
|||
} else if (Token::simpleMatch(afterArguments, "{")) {
|
||||
bodyTok = afterArguments;
|
||||
}
|
||||
for (const Token* c:getCaptures()) {
|
||||
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<const Variable*, std::pair<const Token*, Capture>> explicitCaptures;
|
||||
Capture implicitCapture;
|
||||
|
||||
std::vector<const Token*> getCaptures() {
|
||||
return getArguments(capture);
|
||||
}
|
||||
|
||||
bool isLambda() const {
|
||||
return capture && bodyTok;
|
||||
|
@ -3762,11 +3792,15 @@ static void valueFlowLifetime(TokenList *tokenlist, SymbolDatabase*, ErrorLogger
|
|||
const Scope * bodyScope = lam.bodyTok->scope();
|
||||
|
||||
std::set<const Scope *> scopes;
|
||||
// Avoid capturing a variable twice
|
||||
std::set<nonneg int> varids;
|
||||
|
||||
auto isCapturingVariable = [&](const Token *varTok) {
|
||||
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();
|
||||
|
@ -3777,23 +3811,41 @@ static void valueFlowLifetime(TokenList *tokenlist, SymbolDatabase*, ErrorLogger
|
|||
if (scope->isNestedIn(bodyScope))
|
||||
return false;
|
||||
scopes.insert(scope);
|
||||
varids.insert(var->declarationId());
|
||||
return true;
|
||||
};
|
||||
|
||||
// TODO: Handle explicit capture
|
||||
bool captureByRef = Token::Match(lam.capture, "[ & ]");
|
||||
bool captureByValue = Token::Match(lam.capture, "[ = ]");
|
||||
auto captureVariable = [&](const Token* tok2, Lambda::Capture c, std::function<bool(const Token*)> pred) {
|
||||
if (varids.count(tok->varId()) > 0)
|
||||
return;
|
||||
ErrorPath errorPath;
|
||||
if (c == Lambda::Capture::ByReference) {
|
||||
LifetimeStore{tok2, "Lambda captures variable by reference here.", ValueFlow::Value::LifetimeKind::Lambda} .byRef(
|
||||
tok, tokenlist, errorLogger, settings, pred);
|
||||
} else if (c == Lambda::Capture::ByValue) {
|
||||
LifetimeStore{tok2, "Lambda captures variable by value here.", ValueFlow::Value::LifetimeKind::Lambda} .byVal(
|
||||
tok, tokenlist, errorLogger, settings, pred);
|
||||
}
|
||||
};
|
||||
|
||||
// Handle explicit capture
|
||||
for (const auto& p:lam.explicitCaptures) {
|
||||
const Variable* var = p.first;
|
||||
if (!var)
|
||||
continue;
|
||||
const Token* tok2 = p.second.first;
|
||||
Lambda::Capture c = p.second.second;
|
||||
captureVariable(tok2, c, [](const Token*) {
|
||||
return true;
|
||||
});
|
||||
varids.insert(var->declarationId());
|
||||
}
|
||||
|
||||
for (const Token * tok2 = lam.bodyTok; tok2 != lam.bodyTok->link(); tok2 = tok2->next()) {
|
||||
ErrorPath errorPath;
|
||||
if (captureByRef) {
|
||||
LifetimeStore{tok2, "Lambda captures variable by reference here.", ValueFlow::Value::LifetimeKind::Lambda} .byRef(
|
||||
tok, tokenlist, errorLogger, settings, isCapturingVariable);
|
||||
} else if (captureByValue) {
|
||||
LifetimeStore{tok2, "Lambda captures variable by value here.", ValueFlow::Value::LifetimeKind::Lambda} .byVal(
|
||||
tok, tokenlist, errorLogger, settings, isCapturingVariable);
|
||||
}
|
||||
captureVariable(tok2, lam.implicitCapture, isImplicitCapturingVariable);
|
||||
}
|
||||
|
||||
valueFlowForwardLifetime(tok, tokenlist, errorLogger, settings);
|
||||
}
|
||||
// address of
|
||||
else if (tok->isUnaryOp("&")) {
|
||||
|
|
|
@ -369,7 +369,10 @@ struct LifetimeToken {
|
|||
|
||||
const Token *parseCompareInt(const Token *tok, ValueFlow::Value &true_value, ValueFlow::Value &false_value);
|
||||
|
||||
std::vector<LifetimeToken> getLifetimeTokens(const Token* tok, ValueFlow::Value::ErrorPath errorPath = ValueFlow::Value::ErrorPath{}, int depth = 20);
|
||||
std::vector<LifetimeToken> getLifetimeTokens(const Token* tok,
|
||||
bool escape = false,
|
||||
ValueFlow::Value::ErrorPath errorPath = ValueFlow::Value::ErrorPath{},
|
||||
int depth = 20);
|
||||
|
||||
const Variable* getLifetimeVariable(const Token* tok, ValueFlow::Value::ErrorPath& errorPath, bool* addressOf = nullptr);
|
||||
|
||||
|
|
|
@ -108,14 +108,16 @@ This option is only valid when supplying an input directory. To ignore multiple
|
|||
|
||||
cppcheck -isrc/b -isrc/c
|
||||
|
||||
### Clang parser
|
||||
### Clang parser (experimental)
|
||||
|
||||
By default Cppcheck uses an internal C/C++ parser. However it is possible to use the Clang parser instead.
|
||||
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 our 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 e.g. `--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:
|
||||
|
|
|
@ -148,6 +148,13 @@ 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
|
||||
|
||||
http://cppcheck.sourceforge.net/
|
||||
|
|
15
readme.txt
15
readme.txt
|
@ -113,11 +113,18 @@ Compiling
|
|||
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
|
||||
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
|
||||
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
|
||||
|
||||
|
|
|
@ -1,19 +1,21 @@
|
|||
|
||||
# python3 -m pytest test-clang-import.py
|
||||
# python -m pytest test-clang-import.py
|
||||
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
from testutils import create_gui_project_file, cppcheck
|
||||
|
||||
|
||||
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'argDef: .*', 'argDef: ---', s)
|
||||
s = re.sub(r'nestedIn: .*', 'nestedIn: ---', s)
|
||||
s = re.sub(r'functionScope: .*', 'functionScope: ---', s)
|
||||
s = re.sub(r'definedType: .*', 'definedType: ---', s)
|
||||
s = re.sub(r'classDef: .*', 'classDef: ---', s)
|
||||
s = re.sub(r'needInitialization: .*', 'needInitialization: ---', s)
|
||||
s = re.sub(r'functionOf: .*', 'functionOf: ---', s)
|
||||
s = re.sub(r'0x12345678 Struct', '0x12345678 Class', s)
|
||||
pos1 = s.find(title)
|
||||
assert pos1 > 0
|
||||
pos1 = s.find('\n', pos1) + 1
|
||||
|
@ -24,7 +26,13 @@ def get_debug_section(title, stdout):
|
|||
return s[pos1:pos2-1]
|
||||
|
||||
|
||||
def check_symbol_database(code:str):
|
||||
def check_symbol_database(code):
|
||||
# Only compare symboldatabases if clang is found in PATH
|
||||
try:
|
||||
subprocess.call(['clang', '--version'])
|
||||
except OSError:
|
||||
return
|
||||
|
||||
testfile = 'test.cpp'
|
||||
with open(testfile, 'w+t') as f:
|
||||
f.write(code)
|
||||
|
@ -37,4 +45,9 @@ def check_symbol_database(code:str):
|
|||
def test1():
|
||||
check_symbol_database('int main(){return 0;}')
|
||||
|
||||
def test2():
|
||||
code = 'struct Foo { void f(); }; void Foo::f() {}'
|
||||
check_symbol_database(code)
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1825,6 +1825,50 @@ private:
|
|||
" return g([&]() { return x; });\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"
|
||||
" 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());
|
||||
}
|
||||
|
||||
void danglingLifetimeContainer() {
|
||||
|
@ -2656,6 +2700,22 @@ private:
|
|||
" return std::string(str.c_str(), 1);\n"
|
||||
"}\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());
|
||||
}
|
||||
|
||||
void invalidLifetime() {
|
||||
|
|
|
@ -42,6 +42,9 @@ private:
|
|||
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_template_var);
|
||||
TEST_CASE(ctu);
|
||||
#endif
|
||||
}
|
||||
|
@ -175,6 +178,38 @@ private:
|
|||
"}");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
}
|
||||
|
||||
void uninit_fp_smartptr() {
|
||||
check("void foo() {\n"
|
||||
" std::unique_ptr<std::string> 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_template_var() {
|
||||
check("void foo() {\n"
|
||||
" X*x = DYNAMIC_CAST(X, p);\n"
|
||||
" C<int> c;\n"
|
||||
" f(c);\n"
|
||||
"}");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
}
|
||||
};
|
||||
|
||||
REGISTER_TEST(TestBughuntingChecks)
|
||||
|
|
|
@ -2531,6 +2531,13 @@ private:
|
|||
" ~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());
|
||||
}
|
||||
|
||||
void checkNoMemset(const char code[]) {
|
||||
|
|
|
@ -2310,6 +2310,12 @@ private:
|
|||
" }"
|
||||
"}");
|
||||
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() {
|
||||
|
@ -3306,6 +3312,17 @@ private:
|
|||
"}\n");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
|
||||
check("int foo() {\n"
|
||||
" auto x = getX();\n"
|
||||
" if (x == nullptr)\n"
|
||||
" return 1;\n"
|
||||
" auto y = dynamic_cast<Y*>(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"
|
||||
|
|
|
@ -47,6 +47,9 @@ private:
|
|||
TEST_CASE(importCompileCommands2); // #8563
|
||||
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);
|
||||
TEST_CASE(importCompileCommandsArgumentsSection); // Handle arguments section
|
||||
TEST_CASE(importCompileCommandsNoCommandSection); // gracefully handles malformed json
|
||||
TEST_CASE(importCppcheckGuiProject);
|
||||
|
@ -99,9 +102,11 @@ private:
|
|||
}
|
||||
|
||||
void importCompileCommands1() const {
|
||||
const char json[] = "[ { \"directory\": \"/tmp\","
|
||||
"\"command\": \"gcc -I/tmp -DFILESDIR=\\\\\\\"/usr/local/share/Cppcheck\\\\\\\" -DTEST1 -DTEST2=2 -o /tmp/src.o -c /tmp/src.c\","
|
||||
"\"file\": \"/tmp/src.c\" } ]";
|
||||
const char json[] = R"([{
|
||||
"directory": "/tmp",
|
||||
"command": "gcc -DFILESDIR=\"/usr/local/share/Cppcheck\" -DTEST1 -DTEST2=2 -o /tmp/src.o -c /tmp/src.c",
|
||||
"file": "/tmp/src.c"
|
||||
}])";
|
||||
std::istringstream istr(json);
|
||||
TestImporter importer;
|
||||
importer.importCompileCommands(istr);
|
||||
|
@ -110,9 +115,11 @@ private:
|
|||
}
|
||||
|
||||
void importCompileCommands2() const {
|
||||
const char json[] = "[ { \"directory\": \"/tmp\","
|
||||
"\"command\": \"gcc -c src.c\","
|
||||
"\"file\": \"src.c\" } ]";
|
||||
const char json[] = R"([{
|
||||
"directory": "/tmp",
|
||||
"command": "gcc -c src.c",
|
||||
"file": "src.c"
|
||||
}])";
|
||||
std::istringstream istr(json);
|
||||
TestImporter importer;
|
||||
importer.importCompileCommands(istr);
|
||||
|
@ -121,9 +128,11 @@ private:
|
|||
}
|
||||
|
||||
void importCompileCommands3() const {
|
||||
const char json[] = "[ { \"directory\": \"/tmp/\","
|
||||
"\"command\": \"gcc -c src.c\","
|
||||
"\"file\": \"src.c\" } ]";
|
||||
const char json[] = R"([{
|
||||
"directory": "/tmp/",
|
||||
"command": "gcc -c src.c",
|
||||
"file": "src.c"
|
||||
}])";
|
||||
std::istringstream istr(json);
|
||||
TestImporter importer;
|
||||
importer.importCompileCommands(istr);
|
||||
|
@ -132,15 +141,71 @@ private:
|
|||
}
|
||||
|
||||
void importCompileCommands4() const {
|
||||
const char json[] = "[ { \"directory\": \"/tmp/\","
|
||||
"\"command\": \"gcc -c src.mm\","
|
||||
"\"file\": \"src.mm\" } ]";
|
||||
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 {
|
||||
const char json[] = R"([{
|
||||
"directory": "/tmp",
|
||||
"command": "gcc -DFILESDIR=\"\\\"/home/danielm/cppcheck 2\\\"\" -I\"/home/danielm/cppcheck 2/build/externals/tinyxml\" -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());
|
||||
//FIXME ASSERT_EQUALS("FILESDIR=\"/home/danielm/cppcheck 2\";TEST1=1;TEST2=2", importer.fileSettings.begin()->defines);
|
||||
ASSERT_EQUALS(1, importer.fileSettings.begin()->includePaths.size());
|
||||
ASSERT_EQUALS("/home/danielm/cppcheck 2/build/externals/tinyxml/", importer.fileSettings.begin()->includePaths.front());
|
||||
}
|
||||
|
||||
void importCompileCommandsArgumentsSection() const {
|
||||
const char json[] = "[ { \"directory\": \"/tmp/\","
|
||||
"\"arguments\": [\"gcc\", \"-c\", \"src.c\"],"
|
||||
|
|
|
@ -2232,6 +2232,13 @@ private:
|
|||
"}");
|
||||
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<U2>(0) );\n"
|
||||
"}");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
|
||||
check("class a {\n"
|
||||
" void foo(const int& i) const;\n"
|
||||
|
@ -5303,7 +5310,7 @@ private:
|
|||
check("void f(int* x, bool b) {\n"
|
||||
" if ((!x && b) || (x != 0 && b)) {}\n"
|
||||
"}\n");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
ASSERT_EQUALS("[test.cpp:2]: (style) Opposite expression on both sides of '||'.\n", errout.str());
|
||||
}
|
||||
|
||||
void oppositeExpression() {
|
||||
|
|
|
@ -197,6 +197,7 @@ private:
|
|||
TEST_CASE(template155); // #9539
|
||||
TEST_CASE(template156);
|
||||
TEST_CASE(template157); // #9854
|
||||
TEST_CASE(template158); // daca crash
|
||||
TEST_CASE(template_specialization_1); // #7868 - template specialization template <typename T> struct S<C<T>> {..};
|
||||
TEST_CASE(template_specialization_2); // #7868 - template specialization template <typename T> struct S<C<T>> {..};
|
||||
TEST_CASE(template_enum); // #6299 Syntax error in complex enum declaration (including template)
|
||||
|
@ -2089,24 +2090,66 @@ private:
|
|||
" template<typename T> T foo(T t) { return t; }\n"
|
||||
" template<> char foo<char>(char a) { return a; }\n"
|
||||
" template<> int foo<int>(int a) { return a; }\n"
|
||||
" template short NS2::foo<short>(short);\n"
|
||||
" template long NS1::NS2::foo<long>(long);\n"
|
||||
" }\n"
|
||||
" template float NS2::foo<float>(float);\n"
|
||||
" template bool NS1::NS2::foo<bool>(bool);\n"
|
||||
"}\n"
|
||||
"template double NS1::NS2::foo<double>(double);";
|
||||
const char exp[] = "namespace NS1 { "
|
||||
"namespace NS2 { "
|
||||
"int foo<int> ( int a ) ; "
|
||||
"char foo<char> ( char a ) ; "
|
||||
"short foo<short> ( short t ) ; "
|
||||
"long foo<long> ( long t ) ; "
|
||||
"float foo<float> ( float t ) ; "
|
||||
"bool foo<bool> ( bool t ) ; "
|
||||
"double foo<double> ( double t ) ; "
|
||||
"char foo<char> ( char a ) { return a ; } "
|
||||
"int foo<int> ( int a ) { return a ; } "
|
||||
"} "
|
||||
"} "
|
||||
"short NS1 :: NS2 :: foo<short> ( short t ) { return t ; } "
|
||||
"long NS1 :: NS2 :: foo<long> ( long t ) { return t ; } "
|
||||
"float NS1 :: NS2 :: foo<float> ( float t ) { return t ; } "
|
||||
"bool NS1 :: NS2 :: foo<bool> ( bool t ) { return t ; } "
|
||||
"double NS1 :: NS2 :: foo<double> ( double t ) { return t ; }";
|
||||
ASSERT_EQUALS(exp, tok(code));
|
||||
}
|
||||
{
|
||||
const char code[] = "namespace NS1 {\n"
|
||||
" namespace NS {\n"
|
||||
" template<typename T> T foo(T t) { return t; }\n"
|
||||
" template<> char foo<char>(char a) { return a; }\n"
|
||||
" template<> int foo<int>(int a) { return a; }\n"
|
||||
" template short NS::foo<short>(short);\n"
|
||||
" template long NS1::NS::foo<long>(long);\n"
|
||||
" }\n"
|
||||
" template float NS::foo<float>(float);\n"
|
||||
" template bool NS1::NS::foo<bool>(bool);\n"
|
||||
"}\n"
|
||||
"template double NS1::NS::foo<double>(double);";
|
||||
const char exp[] = "namespace NS1 { "
|
||||
"namespace NS { "
|
||||
"int foo<int> ( int a ) ; "
|
||||
"char foo<char> ( char a ) ; "
|
||||
"short foo<short> ( short t ) ; "
|
||||
"long foo<long> ( long t ) ; "
|
||||
"float foo<float> ( float t ) ; "
|
||||
"bool foo<bool> ( bool t ) ; "
|
||||
"double foo<double> ( double t ) ; "
|
||||
"char foo<char> ( char a ) { return a ; } "
|
||||
"int foo<int> ( int a ) { return a ; } "
|
||||
"} "
|
||||
"} "
|
||||
"short NS1 :: NS :: foo<short> ( short t ) { return t ; } "
|
||||
"long NS1 :: NS :: foo<long> ( long t ) { return t ; } "
|
||||
"float NS1 :: NS :: foo<float> ( float t ) { return t ; } "
|
||||
"bool NS1 :: NS :: foo<bool> ( bool t ) { return t ; } "
|
||||
"double NS1 :: NS :: foo<double> ( double t ) { return t ; }";
|
||||
ASSERT_EQUALS(exp, tok(code));
|
||||
}
|
||||
}
|
||||
|
||||
void template92() {
|
||||
|
@ -3767,6 +3810,214 @@ private:
|
|||
ASSERT_EQUALS(exp, tok(code));
|
||||
}
|
||||
|
||||
void template158() { // daca crash
|
||||
const char code[] = "template <typename> class a0{};\n"
|
||||
"template <typename> class a1{};\n"
|
||||
"template <typename> class a2{};\n"
|
||||
"template <typename> class a3{};\n"
|
||||
"template <typename> class a4{};\n"
|
||||
"template <typename> class a5{};\n"
|
||||
"template <typename> class a6{};\n"
|
||||
"template <typename> class a7{};\n"
|
||||
"template <typename> class a8{};\n"
|
||||
"template <typename> class a9{};\n"
|
||||
"template <typename> class a10{};\n"
|
||||
"template <typename> class a11{};\n"
|
||||
"template <typename> class a12{};\n"
|
||||
"template <typename> class a13{};\n"
|
||||
"template <typename> class a14{};\n"
|
||||
"template <typename> class a15{};\n"
|
||||
"template <typename> class a16{};\n"
|
||||
"template <typename> class a17{};\n"
|
||||
"template <typename> class a18{};\n"
|
||||
"template <typename> class a19{};\n"
|
||||
"template <typename> class a20{};\n"
|
||||
"template <typename> class a21{};\n"
|
||||
"template <typename> class a22{};\n"
|
||||
"template <typename> class a23{};\n"
|
||||
"template <typename> class a24{};\n"
|
||||
"template <typename> class a25{};\n"
|
||||
"template <typename> class a26{};\n"
|
||||
"template <typename> class a27{};\n"
|
||||
"template <typename> class a28{};\n"
|
||||
"template <typename> class a29{};\n"
|
||||
"template <typename> class a30{};\n"
|
||||
"template <typename> class a31{};\n"
|
||||
"template <typename> class a32{};\n"
|
||||
"template <typename> class a33{};\n"
|
||||
"template <typename> class a34{};\n"
|
||||
"template <typename> class a35{};\n"
|
||||
"template <typename> class a36{};\n"
|
||||
"template <typename> class a37{};\n"
|
||||
"template <typename> class a38{};\n"
|
||||
"template <typename> class a39{};\n"
|
||||
"template <typename> class a40{};\n"
|
||||
"template <typename> class a41{};\n"
|
||||
"template <typename> class a42{};\n"
|
||||
"template <typename> class a43{};\n"
|
||||
"template <typename> class a44{};\n"
|
||||
"template <typename> class a45{};\n"
|
||||
"template <typename> class a46{};\n"
|
||||
"template <typename> class a47{};\n"
|
||||
"template <typename> class a48{};\n"
|
||||
"template <typename> class a49{};\n"
|
||||
"template <typename> class a50{};\n"
|
||||
"template <typename> class a51{};\n"
|
||||
"template <typename> class a52{};\n"
|
||||
"template <typename> class a53{};\n"
|
||||
"template <typename> class a54{};\n"
|
||||
"template <typename> class a55{};\n"
|
||||
"template <typename> class a56{};\n"
|
||||
"template <typename> class a57{};\n"
|
||||
"template <typename> class a58{};\n"
|
||||
"template <typename> class a59{};\n"
|
||||
"template <typename> class a60{};\n"
|
||||
"template <typename> class a61{};\n"
|
||||
"template <typename> class a62{};\n"
|
||||
"template <typename> class a63{};\n"
|
||||
"template <typename> class a64{};\n"
|
||||
"template <typename> class a65{};\n"
|
||||
"template <typename> class a66{};\n"
|
||||
"template <typename> class a67{};\n"
|
||||
"template <typename> class a68{};\n"
|
||||
"template <typename> class a69{};\n"
|
||||
"template <typename> class a70{};\n"
|
||||
"template <typename> class a71{};\n"
|
||||
"template <typename> class a72{};\n"
|
||||
"template <typename> class a73{};\n"
|
||||
"template <typename> class a74{};\n"
|
||||
"template <typename> class a75{};\n"
|
||||
"template <typename> class a76{};\n"
|
||||
"template <typename> class a77{};\n"
|
||||
"template <typename> class a78{};\n"
|
||||
"template <typename> class a79{};\n"
|
||||
"template <typename> class a80{};\n"
|
||||
"template <typename> class a81{};\n"
|
||||
"template <typename> class a82{};\n"
|
||||
"template <typename> class a83{};\n"
|
||||
"template <typename> class a84{};\n"
|
||||
"template <typename> class a85{};\n"
|
||||
"template <typename> class a86{};\n"
|
||||
"template <typename> class a87{};\n"
|
||||
"template <typename> class a88{};\n"
|
||||
"template <typename> class a89{};\n"
|
||||
"template <typename> class a90{};\n"
|
||||
"template <typename> class a91{};\n"
|
||||
"template <typename> class a92{};\n"
|
||||
"template <typename> class a93{};\n"
|
||||
"template <typename> class a94{};\n"
|
||||
"template <typename> class a95{};\n"
|
||||
"template <typename> class a96{};\n"
|
||||
"template <typename> class a97{};\n"
|
||||
"template <typename> class a98{};\n"
|
||||
"template <typename> class a99{};\n"
|
||||
"template <typename> class a100{};\n"
|
||||
"template <typename> class b {};\n"
|
||||
"b<a0<int>> d0;\n"
|
||||
"b<a1<int>> d1;\n"
|
||||
"b<a2<int>> d2;\n"
|
||||
"b<a3<int>> d3;\n"
|
||||
"b<a4<int>> d4;\n"
|
||||
"b<a5<int>> d5;\n"
|
||||
"b<a6<int>> d6;\n"
|
||||
"b<a7<int>> d7;\n"
|
||||
"b<a8<int>> d8;\n"
|
||||
"b<a9<int>> d9;\n"
|
||||
"b<a10<int>> d10;\n"
|
||||
"b<a11<int>> d11;\n"
|
||||
"b<a12<int>> d12;\n"
|
||||
"b<a13<int>> d13;\n"
|
||||
"b<a14<int>> d14;\n"
|
||||
"b<a15<int>> d15;\n"
|
||||
"b<a16<int>> d16;\n"
|
||||
"b<a17<int>> d17;\n"
|
||||
"b<a18<int>> d18;\n"
|
||||
"b<a19<int>> d19;\n"
|
||||
"b<a20<int>> d20;\n"
|
||||
"b<a21<int>> d21;\n"
|
||||
"b<a22<int>> d22;\n"
|
||||
"b<a23<int>> d23;\n"
|
||||
"b<a24<int>> d24;\n"
|
||||
"b<a25<int>> d25;\n"
|
||||
"b<a26<int>> d26;\n"
|
||||
"b<a27<int>> d27;\n"
|
||||
"b<a28<int>> d28;\n"
|
||||
"b<a29<int>> d29;\n"
|
||||
"b<a30<int>> d30;\n"
|
||||
"b<a31<int>> d31;\n"
|
||||
"b<a32<int>> d32;\n"
|
||||
"b<a33<int>> d33;\n"
|
||||
"b<a34<int>> d34;\n"
|
||||
"b<a35<int>> d35;\n"
|
||||
"b<a36<int>> d36;\n"
|
||||
"b<a37<int>> d37;\n"
|
||||
"b<a38<int>> d38;\n"
|
||||
"b<a39<int>> d39;\n"
|
||||
"b<a40<int>> d40;\n"
|
||||
"b<a41<int>> d41;\n"
|
||||
"b<a42<int>> d42;\n"
|
||||
"b<a43<int>> d43;\n"
|
||||
"b<a44<int>> d44;\n"
|
||||
"b<a45<int>> d45;\n"
|
||||
"b<a46<int>> d46;\n"
|
||||
"b<a47<int>> d47;\n"
|
||||
"b<a48<int>> d48;\n"
|
||||
"b<a49<int>> d49;\n"
|
||||
"b<a50<int>> d50;\n"
|
||||
"b<a51<int>> d51;\n"
|
||||
"b<a52<int>> d52;\n"
|
||||
"b<a53<int>> d53;\n"
|
||||
"b<a54<int>> d54;\n"
|
||||
"b<a55<int>> d55;\n"
|
||||
"b<a56<int>> d56;\n"
|
||||
"b<a57<int>> d57;\n"
|
||||
"b<a58<int>> d58;\n"
|
||||
"b<a59<int>> d59;\n"
|
||||
"b<a60<int>> d60;\n"
|
||||
"b<a61<int>> d61;\n"
|
||||
"b<a62<int>> d62;\n"
|
||||
"b<a63<int>> d63;\n"
|
||||
"b<a64<int>> d64;\n"
|
||||
"b<a65<int>> d65;\n"
|
||||
"b<a66<int>> d66;\n"
|
||||
"b<a67<int>> d67;\n"
|
||||
"b<a68<int>> d68;\n"
|
||||
"b<a69<int>> d69;\n"
|
||||
"b<a70<int>> d70;\n"
|
||||
"b<a71<int>> d71;\n"
|
||||
"b<a72<int>> d72;\n"
|
||||
"b<a73<int>> d73;\n"
|
||||
"b<a74<int>> d74;\n"
|
||||
"b<a75<int>> d75;\n"
|
||||
"b<a76<int>> d76;\n"
|
||||
"b<a77<int>> d77;\n"
|
||||
"b<a78<int>> d78;\n"
|
||||
"b<a79<int>> d79;\n"
|
||||
"b<a80<int>> d80;\n"
|
||||
"b<a81<int>> d81;\n"
|
||||
"b<a82<int>> d82;\n"
|
||||
"b<a83<int>> d83;\n"
|
||||
"b<a84<int>> d84;\n"
|
||||
"b<a85<int>> d85;\n"
|
||||
"b<a86<int>> d86;\n"
|
||||
"b<a87<int>> d87;\n"
|
||||
"b<a88<int>> d88;\n"
|
||||
"b<a89<int>> d89;\n"
|
||||
"b<a90<int>> d90;\n"
|
||||
"b<a91<int>> d91;\n"
|
||||
"b<a92<int>> d92;\n"
|
||||
"b<a93<int>> d93;\n"
|
||||
"b<a94<int>> d94;\n"
|
||||
"b<a95<int>> d95;\n"
|
||||
"b<a96<int>> d96;\n"
|
||||
"b<a97<int>> d97;\n"
|
||||
"b<a98<int>> d98;\n"
|
||||
"b<a99<int>> d99;\n"
|
||||
"b<a100<int>> d100;";
|
||||
// don't bother checking the output because this is not instantiated properly
|
||||
tok(code); // don't crash
|
||||
}
|
||||
|
||||
void template_specialization_1() { // #7868 - template specialization template <typename T> struct S<C<T>> {..};
|
||||
const char code[] = "template <typename T> struct C {};\n"
|
||||
"template <typename T> struct S {a};\n"
|
||||
|
|
|
@ -378,6 +378,8 @@ private:
|
|||
TEST_CASE(findFunctionExternC);
|
||||
TEST_CASE(findFunctionGlobalScope); // ::foo
|
||||
|
||||
TEST_CASE(overloadedFunction1);
|
||||
|
||||
TEST_CASE(valueTypeMatchParameter); // ValueType::matchParameter
|
||||
|
||||
TEST_CASE(noexceptFunction1);
|
||||
|
@ -6178,6 +6180,19 @@ private:
|
|||
ASSERT(bar->function());
|
||||
}
|
||||
|
||||
void overloadedFunction1() {
|
||||
GET_SYMBOL_DB("struct S {\n"
|
||||
" int operator()(int);\n"
|
||||
"};\n"
|
||||
"\n"
|
||||
"void foo(S x) {\n"
|
||||
" x(123);\n"
|
||||
"}");
|
||||
const Token *tok = Token::findsimplematch(tokenizer.tokens(), "x . operator() ( 123 )");
|
||||
ASSERT(tok);
|
||||
ASSERT(tok->tokAt(2)->function());
|
||||
}
|
||||
|
||||
void valueTypeMatchParameter() {
|
||||
ValueType vt_int(ValueType::Sign::SIGNED, ValueType::Type::INT, 0);
|
||||
ValueType vt_const_int(ValueType::Sign::SIGNED, ValueType::Type::INT, 0, 1);
|
||||
|
@ -6291,15 +6306,10 @@ private:
|
|||
" :a(std::move(b.a)) { }\n"
|
||||
"};\n");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
ASSERT_EQUALS(true, db != nullptr); // not null
|
||||
|
||||
if (db) {
|
||||
const Scope *b = db->findScopeByName("B");
|
||||
ASSERT_EQUALS(true, b != nullptr);
|
||||
if (b) {
|
||||
CLASS_FUNC(B, b, true);
|
||||
}
|
||||
}
|
||||
ASSERT(db != nullptr); // not null
|
||||
const Scope *b = db->findScopeByName("B");
|
||||
ASSERT(b != nullptr);
|
||||
CLASS_FUNC(B, b, true);
|
||||
}
|
||||
|
||||
#define FUNC_THROW(x) do { \
|
||||
|
@ -6314,14 +6324,12 @@ private:
|
|||
"void func3() throw(int);\n"
|
||||
"void func4() throw(int) { }\n");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
ASSERT_EQUALS(true, db != nullptr); // not null
|
||||
ASSERT(db != nullptr); // not null
|
||||
|
||||
if (db) {
|
||||
FUNC_THROW(func1);
|
||||
FUNC_THROW(func2);
|
||||
FUNC_THROW(func3);
|
||||
FUNC_THROW(func4);
|
||||
}
|
||||
FUNC_THROW(func1);
|
||||
FUNC_THROW(func2);
|
||||
FUNC_THROW(func3);
|
||||
FUNC_THROW(func4);
|
||||
}
|
||||
|
||||
#define CLASS_FUNC_THROW(x, y) do { \
|
||||
|
@ -6814,6 +6822,7 @@ private:
|
|||
ASSERT_EQUALS("A::BC *", typeOf("namespace A { struct BC { int b; int c; }; }; struct A::BC abc; x=&abc;", "&"));
|
||||
ASSERT_EQUALS("A::BC *", typeOf("namespace A { struct BC { int b; int c; }; }; struct A::BC *abc; x=abc+1;", "+"));
|
||||
ASSERT_EQUALS("signed int", typeOf("auto a(int& x, int& y) { return x + y; }", "+"));
|
||||
ASSERT_EQUALS("signed int", typeOf("auto a(int& x) { return x << 1; }", "<<"));
|
||||
ASSERT_EQUALS("signed int", typeOf("void a(int& x, int& y) { x = y; }", "=")); //Debatably this should be a signed int & but we'll stick with the current behavior for now
|
||||
ASSERT_EQUALS("signed int", typeOf("auto a(int* y) { return *y; }", "*")); //Debatably this should be a signed int & but we'll stick with the current behavior for now
|
||||
|
||||
|
|
|
@ -1116,6 +1116,9 @@ private:
|
|||
|
||||
givenACodeSampleToTokenize data5("void f() { return U\"a\"; }");
|
||||
ASSERT_EQUALS("returnU\"a\"", data5.tokens()->tokAt(5)->expressionString());
|
||||
|
||||
givenACodeSampleToTokenize data6("x = \"\\0\\x1\\x2\\x3\\x4\\x5\\x6\\x7\";");
|
||||
ASSERT_EQUALS("x=\"\\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x07\"", data6.tokens()->next()->expressionString());
|
||||
}
|
||||
|
||||
void hasKnownIntValue() {
|
||||
|
|
|
@ -416,6 +416,10 @@ private:
|
|||
TEST_CASE(simplifyOperatorName26);
|
||||
TEST_CASE(simplifyOperatorName27);
|
||||
|
||||
TEST_CASE(simplifyOverloadedOperators1);
|
||||
TEST_CASE(simplifyOverloadedOperators2); // (*this)(123)
|
||||
TEST_CASE(simplifyOverloadedOperators3); // #9881 - hang
|
||||
|
||||
TEST_CASE(simplifyNullArray);
|
||||
|
||||
// Some simple cleanups of unhandled macros in the global scope
|
||||
|
@ -6636,6 +6640,50 @@ private:
|
|||
tokenizeAndStringify(code));
|
||||
}
|
||||
|
||||
void simplifyOverloadedOperators1() {
|
||||
const char code[] = "struct S { void operator()(int); };\n"
|
||||
"\n"
|
||||
"void foo(S x) {\n"
|
||||
" x(123);\n"
|
||||
"}";
|
||||
ASSERT_EQUALS("struct S { void operator() ( int ) ; } ;\n"
|
||||
"\n"
|
||||
"void foo ( S x ) {\n"
|
||||
"x . operator() ( 123 ) ;\n"
|
||||
"}",
|
||||
tokenizeAndStringify(code));
|
||||
}
|
||||
|
||||
void simplifyOverloadedOperators2() { // #9879 - (*this)(123);
|
||||
const char code[] = "struct S {\n"
|
||||
" void operator()(int);\n"
|
||||
" void foo() { (*this)(123); }\n"
|
||||
"};\n";
|
||||
ASSERT_EQUALS("struct S {\n"
|
||||
"void operator() ( int ) ;\n"
|
||||
"void foo ( ) { ( * this ) . operator() ( 123 ) ; }\n"
|
||||
"} ;",
|
||||
tokenizeAndStringify(code));
|
||||
}
|
||||
|
||||
void simplifyOverloadedOperators3() { // #9881
|
||||
const char code[] = "struct Func { double operator()(double x) const; };\n"
|
||||
"void foo(double, double);\n"
|
||||
"void test() {\n"
|
||||
" Func max;\n"
|
||||
" double y = 0;\n"
|
||||
" foo(0, max(y));\n"
|
||||
"}";
|
||||
ASSERT_EQUALS("struct Func { double operator() ( double x ) const ; } ;\n"
|
||||
"void foo ( double , double ) ;\n"
|
||||
"void test ( ) {\n"
|
||||
"Func max ;\n"
|
||||
"double y ; y = 0 ;\n"
|
||||
"foo ( 0 , max . operator() ( y ) ) ;\n"
|
||||
"}",
|
||||
tokenizeAndStringify(code));
|
||||
}
|
||||
|
||||
void simplifyNullArray() {
|
||||
ASSERT_EQUALS("* ( foo . bar [ 5 ] ) = x ;", tokenizeAndStringify("0[foo.bar[5]] = x;"));
|
||||
}
|
||||
|
@ -7895,7 +7943,7 @@ private:
|
|||
// `-(
|
||||
// `-{
|
||||
|
||||
ASSERT_EQUALS("x{([( ai=", testAst("x([&a](int i){a=i;});"));
|
||||
ASSERT_EQUALS("x{(a&[( ai=", testAst("x([&a](int i){a=i;});"));
|
||||
ASSERT_EQUALS("{([(return 0return", testAst("return [](){ return 0; }();"));
|
||||
|
||||
// noexcept
|
||||
|
@ -7903,40 +7951,40 @@ private:
|
|||
|
||||
// ->
|
||||
ASSERT_EQUALS("{([(return 0return", testAst("return []() -> int { return 0; }();"));
|
||||
ASSERT_EQUALS("{([(return 0return", testAst("return [something]() -> int { return 0; }();"));
|
||||
ASSERT_EQUALS("{(something[(return 0return", testAst("return [something]() -> int { return 0; }();"));
|
||||
ASSERT_EQUALS("{([cd,(return 0return", testAst("return [](int a, int b) -> int { return 0; }(c, d);"));
|
||||
ASSERT_EQUALS("{([return", testAst("return []() -> decltype(0) {};"));
|
||||
ASSERT_EQUALS("x{([=", testAst("x = [&]()->std::string const & {};"));
|
||||
ASSERT_EQUALS("x{(&[=", testAst("x = [&]()->std::string const & {};"));
|
||||
ASSERT_EQUALS("f{([=", testAst("f = []() -> foo* {};"));
|
||||
ASSERT_EQUALS("f{([=", testAst("f = [](void) mutable -> foo* {};"));
|
||||
ASSERT_EQUALS("f{([=", testAst("f = []() mutable {};"));
|
||||
|
||||
ASSERT_EQUALS("x{([= 0return", testAst("x = [](){return 0; };"));
|
||||
|
||||
ASSERT_EQUALS("ab{[(= cd=", testAst("a = b([&]{c=d;});"));
|
||||
ASSERT_EQUALS("ab{&[(= cd=", testAst("a = b([&]{c=d;});"));
|
||||
|
||||
// 8628
|
||||
ASSERT_EQUALS("f{([( switchx( 1case y++", testAst("f([](){switch(x){case 1:{++y;}}});"));
|
||||
|
||||
ASSERT_EQUALS("{([{return ab=",
|
||||
ASSERT_EQUALS("{(=[{return ab=",
|
||||
testAst("return {\n"
|
||||
" [=]() {\n"
|
||||
" a = b;\n"
|
||||
" }\n"
|
||||
"};\n"));
|
||||
ASSERT_EQUALS("{[{return ab=",
|
||||
ASSERT_EQUALS("{=[{return ab=",
|
||||
testAst("return {\n"
|
||||
" [=] {\n"
|
||||
" a = b;\n"
|
||||
" }\n"
|
||||
"};\n"));
|
||||
ASSERT_EQUALS("{([{return ab=",
|
||||
ASSERT_EQUALS("{(=[{return ab=",
|
||||
testAst("return {\n"
|
||||
" [=]() -> int {\n"
|
||||
" a=b;\n"
|
||||
" }\n"
|
||||
"}"));
|
||||
ASSERT_EQUALS("{([{return ab=",
|
||||
ASSERT_EQUALS("{(=[{return ab=",
|
||||
testAst("return {\n"
|
||||
" [=]() mutable -> int {\n"
|
||||
" a=b;\n"
|
||||
|
@ -7944,12 +7992,13 @@ private:
|
|||
"}"));
|
||||
|
||||
// daca@home hang
|
||||
ASSERT_EQUALS("a{([= 0return b{([= fori0=i10!=i++;;(",
|
||||
ASSERT_EQUALS("a{(&[= 0return b{(=[= fori0=i10!=i++;;(",
|
||||
testAst("a = [&]() -> std::pair<int, int> { return 0; };\n"
|
||||
"b = [=]() { for (i = 0; i != 10; ++i); };"));
|
||||
|
||||
// #9662
|
||||
ASSERT_EQUALS("b{[{ stdunique_ptr::0nullptrnullptr:?{", testAst("auto b{[] { std::unique_ptr<void *>{0 ? nullptr : nullptr}; }};"));
|
||||
ASSERT_EQUALS("a( b{[=", testAst("void a() { [b = [] { ; }] {}; }"));
|
||||
|
||||
}
|
||||
|
||||
|
@ -8126,6 +8175,9 @@ private:
|
|||
|
||||
ASSERT_THROW_EQUALS(tokenizeAndStringify("void f() { assert(a==()); }"), InternalError, "syntax error: ==()");
|
||||
ASSERT_THROW_EQUALS(tokenizeAndStringify("void f() { assert(a+()); }"), InternalError, "syntax error: +()");
|
||||
|
||||
// #9445 - typeof is not a keyword in C
|
||||
ASSERT_NO_THROW(tokenizeAndStringify("void foo() { char *typeof, *value; }", false, false, Settings::Native, "test.c"));
|
||||
}
|
||||
|
||||
|
||||
|
@ -8275,6 +8327,19 @@ private:
|
|||
" }\n"
|
||||
"};\n"));
|
||||
|
||||
// #9858
|
||||
ASSERT_NO_THROW(tokenizeAndStringify(
|
||||
"struct a {\n"
|
||||
" struct b {};\n"
|
||||
"};\n"
|
||||
"void c(a::b, a::b);\n"
|
||||
"void g(a::b f) { c(f, {a::b{}}); }\n"
|
||||
"template <class> void h() {\n"
|
||||
" int e;\n"
|
||||
" for (int d = 0; d < e; d++)\n"
|
||||
" ;\n"
|
||||
"}\n"));
|
||||
|
||||
ASSERT_NO_THROW(tokenizeAndStringify("a<b?0:1>()==3;"));
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue