This commit is contained in:
Paul 2020-09-08 11:00:57 -05:00
commit 1be67ea008
53 changed files with 1370 additions and 463 deletions

View File

@ -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
@ -52,7 +67,7 @@ jobs:
- name: Validate
run: |
make -j$(nproc) validateCFG validatePlatforms
- name: Build GUI on ubuntu
if: matrix.os == 'ubuntu-latest'
run: |
@ -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

39
.github/workflows/asan.yml vendored Normal file
View File

@ -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

View File

@ -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'

View File

@ -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

39
.github/workflows/ubsan.yml vendored Normal file
View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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))

View File

@ -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)

View File

@ -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
{

View File

@ -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;
}

View File

@ -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 -->

View File

@ -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>

View File

@ -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"

View File

@ -445,7 +445,7 @@
<item>
<widget class="QRadioButton" name="mBtnClangParser">
<property name="text">
<string>Clang</string>
<string>Clang (experimental)</string>
</property>
</widget>
</item>

View File

@ -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")

View File

@ -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;

View File

@ -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))

View File

@ -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;
}
}
}

View File

@ -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);

View File

@ -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)

View File

@ -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;

View File

@ -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];

View File

@ -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) {

View File

@ -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();

View File

@ -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);
}

View File

@ -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);
};

View File

@ -37,6 +37,7 @@ Settings::Settings()
checkLibrary(false),
checkUnusedTemplates(false),
clang(false),
clangExecutable("clang"),
clangTidy(false),
daca(false),
debugBugHunting(false),

View File

@ -115,6 +115,9 @@ public:
/** Use Clang */
bool clang;
/** Custom Clang executable */
std::string clangExecutable;
/** Use clang-tidy */
bool clangTidy;

View File

@ -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";

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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();

View File

@ -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)

View File

@ -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()
{

View File

@ -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
*/

View File

@ -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();

View File

@ -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("&")) {

View File

@ -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);

View File

@ -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:

View File

@ -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/

View File

@ -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

View File

@ -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)

View File

@ -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() {

View File

@ -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)

View File

@ -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[]) {

View File

@ -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"

View File

@ -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\"],"

View File

@ -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() {

View File

@ -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"

View File

@ -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

View File

@ -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() {

View File

@ -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;"));
}